hy/docs/extra/anaphoric.rst

258 lines
5.4 KiB
ReStructuredText
Raw Normal View History

================
2013-11-30 01:59:20 +01:00
Anaphoric Macros
================
.. versionadded:: 0.9.12
2013-11-30 01:59:20 +01:00
The anaphoric macros module makes functional programming in Hy very
concise and easy to read.
An anaphoric macro is a type of programming macro that
deliberately captures some form supplied to the macro which may be
referred to by an anaphor (an expression referring to another).
2016-09-04 22:35:46 +02:00
-- Wikipedia (https://en.wikipedia.org/wiki/Anaphoric_macro)
2013-11-30 01:59:20 +01:00
To use these macros you need to require the ``hy.extra.anaphoric`` module like so:
``(require [hy.extra.anaphoric [*]])``
2013-12-26 02:28:32 +01:00
These macros are implemented by replacing any use of the designated
anaphoric symbols (``it``, in most cases) with a gensym. Consequently,
it's unwise to nest these macros where symbol replacement is happening.
Symbol replacement typically takes place in ``body`` or ``form``
parameters, where the output of the expression may be returned. It is also
recommended to avoid using an affected symbol as something other than a
variable name, as in ``(print "My favorite Stephen King book is" 'it)``.
2013-12-26 02:28:32 +01:00
.. _ap-if:
ap-if
=====
2013-12-26 02:28:32 +01:00
Usage: ``(ap-if test-form then-form else-form)``
2013-12-26 02:28:32 +01:00
As :ref:`if <if>`, but the result of the test form is named ``it`` in
the subsequent forms. As with ``if``, the else-clause is optional.
.. code-block:: hy
=> (import os)
=> (ap-if (.get os.environ "PYTHONPATH")
... (print "Your PYTHONPATH is" it))
2013-12-26 02:28:32 +01:00
2013-11-30 01:59:20 +01:00
.. _ap-each:
ap-each
=======
2013-11-30 01:59:20 +01:00
Usage: ``(ap-each xs body…)``
2013-11-30 01:59:20 +01:00
Evaluate the body forms for each element ``it`` of ``xs`` and return
``None``.
.. code-block:: hy
=> (ap-each [1 2 3] (print it))
1
2
3
2013-11-30 01:59:20 +01:00
.. _ap-each-while:
ap-each-while
=============
Usage: ``(ap-each-while xs pred body…)``
2013-11-30 01:59:20 +01:00
As ``ap-each``, but the form ``pred`` is run before the body forms on
each iteration, and the loop ends if ``pred`` is false.
2013-11-30 01:59:20 +01:00
.. code-block:: hy
2013-11-30 01:59:20 +01:00
=> (ap-each-while [1 2 3 4 5 6] (< it 4) (print it))
1
2
3
.. _ap-map:
ap-map
======
Usage: ``(ap-map form xs)``
2013-11-30 01:59:20 +01:00
Create a generator like :py:func:`map` that yields each result of ``form``
evaluated with ``it`` bound to successive elements of ``xs``.
2013-11-30 01:59:20 +01:00
.. code-block:: hy
2013-11-30 01:59:20 +01:00
=> (list (ap-map (* it 2) [1 2 3]))
[2, 4, 6]
.. _ap-map-when:
ap-map-when
===========
Usage: ``(ap-map-when predfn rep xs)``
2013-11-30 01:59:20 +01:00
As ``ap-map``, but the predicate function ``predfn`` (yes, that's a
function, not an anaphoric form) is applied to each ``it``, and the
anaphoric mapping form ``rep`` is only applied if the predicate is true.
Otherwise, ``it`` is yielded unchanged.
2013-11-30 01:59:20 +01:00
.. code-block:: hy
2013-11-30 01:59:20 +01:00
=> (list (ap-map-when odd? (* it 2) [1 2 3 4]))
[2, 2, 6, 4]
=> (list (ap-map-when even? (* it 2) [1 2 3 4]))
[1, 4, 3, 8]
.. _ap-filter:
ap-filter
=========
Usage: ``(ap-filter form xs)``
2013-11-30 01:59:20 +01:00
The :py:func:`filter` equivalent of ``ap-map``.
2013-11-30 01:59:20 +01:00
.. code-block:: hy
2013-11-30 01:59:20 +01:00
=> (list (ap-filter (> (* it 2) 6) [1 2 3 4 5]))
[4, 5]
.. _ap-reject:
ap-reject
=========
Usage: ``(ap-reject form xs)``
Equivalent to ``(ap-filter (not form) xs)``.
.. code-block:: hy
=> (list (ap-reject (> (* it 2) 6) [1 2 3 4 5]))
[1, 2, 3]
.. _ap-dotimes:
ap-dotimes
==========
Usage: ``(ap-dotimes n body…)``
Equivalent to ``(ap-each (range n) body…)``.
.. code-block:: hy
=> (setv n [])
=> (ap-dotimes 3 (.append n it))
=> n
[0, 1, 2]
.. _ap-first:
ap-first
========
Usage: ``(ap-first form xs)``
Evaluate the predicate ``form`` for each element ``it`` of ``xs``. When
the predicate is true, stop and return ``it``. If the predicate is never
true, return ``None``.
.. code-block:: hy
=> (ap-first (> it 5) (range 10))
6
.. _ap-last:
ap-last
========
Usage: ``(ap-last form list)``
Evaluate the predicate ``form`` for every element ``it`` of ``xs``.
Return the last element for which the predicate is true, or ``None`` if
there is no such element.
.. code-block:: hy
=> (ap-last (> it 5) (range 10))
9
.. _ap-reduce:
ap-reduce
=========
Usage: ``(ap-reduce form xs &optional initial-value)``
This macro is an anaphoric version of :py:func:`reduce`. It works as
follows:
- Bind ``acc`` to the first element of ``xs``, bind ``it`` to the
second, and evaluate ``form``.
- Bind ``acc`` to the result, bind ``it`` to the third value of ``xs``,
and evaluate ``form`` again.
- Bind ``acc`` to the result, and continue until ``xs`` is exhausted.
If ``initial-value`` is supplied, the process instead begins with
``acc`` set to ``initial-value`` and ``it`` set to the first element of
``xs``.
.. code-block:: hy
=> (ap-reduce (+ it acc) (range 10))
45
2017-10-23 03:53:05 +02:00
.. _#%
2017-10-23 03:53:05 +02:00
#%
==
Usage: ``#% expr``
2017-10-26 04:46:38 +02:00
Makes an expression into a function with an implicit ``%`` parameter list.
2017-10-23 05:36:30 +02:00
2017-10-26 04:46:38 +02:00
A ``%i`` symbol designates the (1-based) *i* th parameter (such as ``%3``).
Only the maximum ``%i`` determines the number of ``%i`` parameters--the
others need not appear in the expression.
2017-10-23 05:36:30 +02:00
``%*`` and ``%**`` name the ``&rest`` and ``&kwargs`` parameters, respectively.
2017-10-26 04:46:38 +02:00
.. code-block:: hy
=> (#%[%1 %6 42 [%2 %3] %* %4] 1 2 3 4 555 6 7 8)
[1, 6, 42, [2, 3], (7, 8), 4]
=> (#% %** :foo 2)
{"foo": 2}
When used on an s-expression,
``#%`` is similar to Clojure's anonymous function literals--``#()``.
.. code-block:: hy
2017-10-26 04:46:38 +02:00
=> (setv add-10 #%(+ 10 %1))
=> (add-10 6)
16
``#%`` determines the parameter list by the presence of a ``%*`` or ``%**``
symbol and by the maximum ``%i`` symbol found *anywhere* in the expression,
so nesting of ``#%`` forms is not recommended.