contrib/anaphoric: More anaphoric macros added

* hy/contrib/anaphoric.hy: The following anaphoric macros have been
  added
  `ap-reject` : Opposite of ap-filter, yields the elements when a `pred`
  evaluates to false
  `ap-dotimes` : Execute body forms (possibly for side-effects) n times
  with `it` bound from 0 to n-1
  `ap-first` : return the first element that passes predicate
  `ap-last`  : return the last element that passes predicate
  `ap-reduce`: anaphoric form of reduce that allows `acc` and `it` to
  create a function that is applied over the list

* docs/contrib/anaphoric.rst: updated docs to reflect these changes

* tests/__init__.py: updated to explicitly include tests for anaphoric
  macros
This commit is contained in:
Abhishek L 2013-12-26 00:48:09 +05:30 committed by Berker Peksag
parent 744cd71171
commit c69c14cc7d
5 changed files with 164 additions and 2 deletions

View File

@ -1,6 +1,8 @@
====================
================
Anaphoric Macros
====================
================
.. versionadded:: 0.9.12
The anaphoric macros module makes functional programming in Hy very
concise and easy to read.
@ -94,3 +96,90 @@ the current element in the iteration.
[4, 5]
.. _ap-reject:
ap-reject
=========
Usage: ``(ap-reject form list)``
This function does the opposite of ``ap-filter``, it rejects the
elements passing the predicate . The special name ``it`` is bound to
the current element in the iteration.
.. code-block:: clojure
=> (list (ap-reject (> (* it 2) 6) [1 2 3 4 5]))
[1, 2, 3]
.. _ap-dotimes:
ap-dotimes
==========
Usage ``(ap-dotimes n body)``
This function evaluates the body *n* times, with the special
variable ``it`` bound from *0* to *1-n*. It is useful for side-effects.
.. code-block:: clojure
=> (setv n [])
=> (ap-dotimes 3 (.append n it))
=> n
[0, 1, 2]
.. _ap-first:
ap-first
========
Usage ``(ap-first predfn list)``
This function returns the first element that passes the predicate or
``None``, with the special variable ``it`` bound to the current element in
iteration.
.. code-block:: clojure
=>(ap-first (> it 5) (range 10))
6
.. _ap-last:
ap-last
========
Usage ``(ap-last predfn list)``
This function returns the last element that passes the predicate or
``None``, with the special variable ``it`` bound to the current element in
iteration.
.. code-block:: clojure
=>(ap-last (> it 5) (range 10))
9
.. _ap-reduce:
ap-reduce
=========
Usage ``(ap-reduce form list &optional initial-value)``
This function returns the result of applying form to the first 2
elements in the body and applying the result and the 3rd element
etc. until the list is exhausted. Optionally an initial value can be
supplied so the function will be applied to initial value and the
first element instead. This exposes the element being iterated as
``it`` and the current accumulated value as ``acc``.
.. code-block:: clojure
=>(ap-reduce (+ it acc) (range 10))
45

View File

@ -1,6 +1,7 @@
;;; Hy anaphoric macros
;;
;; Copyright (c) 2013 James King <james@agentultra.com>
;; 2013 Abhishek L <abhishek.lekshmanan@gmail.com>
;;
;; Permission is hereby granted, free of charge, to any person obtaining a
;; copy of this software and associated documentation files (the "Software"),
@ -61,3 +62,42 @@
(foreach [val ~lst]
(if (pred val)
(yield val)))))
(defmacro ap-reject [form lst]
"Yield elements returned when the predicate form evaluates to False"
`(ap-filter (not ~form) ~lst))
(defmacro ap-dotimes [n &rest body]
"Execute body for side effects `n' times, with it bound from 0 to n-1"
(unless (numeric? n)
(raise (TypeError (.format "{0!r} is not a number" n))))
`(ap-each (range ~n) ~@body))
(defmacro ap-first [predfn lst]
"Yield the first element that passes `predfn`"
`(let [[n (gensym)]]
(ap-each ~lst (when ~predfn (setv n it) (break)))
n))
(defmacro ap-last [predfn lst]
"Yield the last element that passes `predfn`"
`(let [[n (gensym)]]
(ap-each ~lst (none? n)
(when ~predfn
(setv n it)))
n))
(defmacro ap-reduce [form lst &optional [initial-value None]]
"Anaphoric form of reduce, `acc' and `it' can be used for a form"
(if (none? initial-value)
`(let [[acc (car ~lst)]]
(ap-each (cdr ~lst) (setv acc ~form))
acc)
`(let [[acc ~initial-value]]
(ap-each ~lst (setv acc ~form))
acc)))

View File

@ -12,3 +12,4 @@ from .native_tests.when import * # noqa
from .native_tests.with_decorator import * # noqa
from .native_tests.core import * # noqa
from .native_tests.reader_macros import * # noqa
from .native_tests.contrib.anaphoric import * # noqa

View File

View File

@ -61,3 +61,35 @@
[3 4])
(assert-equal (list (ap-filter (even? it) [1 2 3 4]))
[2 4]))
(defn test-ap-reject []
"NATIVE: testing anaphoric filter"
(assert-equal (list (ap-reject (> it 2) [1 2 3 4]))
[1 2])
(assert-equal (list (ap-reject (even? it) [1 2 3 4]))
[1 3]))
(defn test-ap-dotimes []
"NATIVE: testing anaphoric dotimes"
(assert-equal (let [[n []]] (ap-dotimes 3 (.append n 3)) n)
[3 3 3])
(assert-equal (let [[n []]] (ap-dotimes 3 (.append n it)) n)
[0 1 2]))
(defn test-ap-first []
"NATIVE: testing anaphoric first"
(assert-equal (ap-first (> it 5) (range 10)) 6)
(assert-equal (ap-first (even? it) [1 2 3 4]) 2))
(defn test-ap-last []
"NATIVE: testing anaphoric last"
(assert-equal (ap-last (> it 5) (range 10)) 9)
(assert-equal (ap-last (even? it) [1 2 3 4]) 4))
(defn test-ap-reduce []
"NATIVE: testing anaphoric reduce"
(assert-equal (ap-reduce (* acc it) [1 2 3]) 6)
(assert-equal (ap-reduce (* acc it) [1 2 3] 6) 36)
(assert-equal (ap-reduce (+ acc " on " it) ["Hy" "meth"])
"Hy on meth")
(assert-equal (ap-reduce (+ acc it) [] 1) 1))