Merge branch 'seq'
This commit is contained in:
commit
b3d7069fb3
@ -12,4 +12,5 @@ Contents:
|
|||||||
flow
|
flow
|
||||||
loop
|
loop
|
||||||
multi
|
multi
|
||||||
|
sequences
|
||||||
walk
|
walk
|
||||||
|
81
docs/contrib/sequences.rst
Normal file
81
docs/contrib/sequences.rst
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
==============
|
||||||
|
Lazy sequences
|
||||||
|
==============
|
||||||
|
|
||||||
|
.. versionadded:: 0.12.0
|
||||||
|
|
||||||
|
The sequences module contains a few macros for declaring sequences that are
|
||||||
|
evaluated only as much as the client code requires. Unlike generators, they
|
||||||
|
allow accessing the same element multiple times. They cache calculated values,
|
||||||
|
and the implementation allows for recursive definition of sequences without
|
||||||
|
resulting in recursive computation.
|
||||||
|
|
||||||
|
To use these macros, you need to require them and import some other names like
|
||||||
|
so:
|
||||||
|
|
||||||
|
.. code-block:: hy
|
||||||
|
|
||||||
|
(require [hy.contrib.sequences [defseq seq]])
|
||||||
|
(import [hy.contrib.sequences [Sequence end-sequence]])
|
||||||
|
|
||||||
|
The simplest sequence can be defined as ``(seq [n] n)``. This defines a sequence
|
||||||
|
that starts as ``[0 1 2 3 ...]`` and continues forever. In order to define a
|
||||||
|
finite sequence, you need to call ``end-sequence`` to signal the end of the
|
||||||
|
sequence:
|
||||||
|
|
||||||
|
.. code-block:: hy
|
||||||
|
|
||||||
|
(seq [n]
|
||||||
|
"sequence of 5 integers"
|
||||||
|
(cond [(< n 5) n]
|
||||||
|
[True (end-sequence)]))
|
||||||
|
|
||||||
|
This creates the following sequence: ``[0 1 2 3 4]``. For such a sequence,
|
||||||
|
``len`` returns the amount of items in the sequence and negative indexing is
|
||||||
|
supported. Because both of these require evaluating the whole sequence, calling
|
||||||
|
one on an infinite sequence would take forever (or at least until available
|
||||||
|
memory has been exhausted).
|
||||||
|
|
||||||
|
Sequences can be defined recursively. For example, the Fibonacci sequence could
|
||||||
|
be defined as:
|
||||||
|
|
||||||
|
.. code-block:: hy
|
||||||
|
|
||||||
|
(defseq fibonacci [n]
|
||||||
|
"infinite sequence of fibonacci numbers"
|
||||||
|
(cond [(= n 0) 0]
|
||||||
|
[(= n 1) 1]
|
||||||
|
[True (+ (get fibonacci (- n 1))
|
||||||
|
(get fibonacci (- n 2)))]))
|
||||||
|
|
||||||
|
This results in the sequence ``[0 1 1 2 3 5 8 13 21 34 ...]``.
|
||||||
|
|
||||||
|
.. _seq:
|
||||||
|
|
||||||
|
seq
|
||||||
|
===
|
||||||
|
|
||||||
|
Usage: ``(seq [n] (* n n)``
|
||||||
|
|
||||||
|
Creates a sequence defined in terms of ``n``.
|
||||||
|
|
||||||
|
.. _defseq:
|
||||||
|
|
||||||
|
defseq
|
||||||
|
======
|
||||||
|
|
||||||
|
Usage: ``(defseq numbers [n] n)``
|
||||||
|
|
||||||
|
Creates a sequence defined in terms of ``n`` and assigns it to a given name.
|
||||||
|
|
||||||
|
.. _end-sequence:
|
||||||
|
|
||||||
|
end-sequence
|
||||||
|
============
|
||||||
|
|
||||||
|
Usage: ``(seq [n] (if (< n 5) n (end-sequence)))``
|
||||||
|
|
||||||
|
Signals the end of a sequence when an iterator reaches the given
|
||||||
|
point of the sequence. Internally, this is done by raising
|
||||||
|
``IndexError``, catching that in the iterator, and raising
|
||||||
|
``StopIteration``.
|
79
hy/contrib/sequences.hy
Normal file
79
hy/contrib/sequences.hy
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
;; Copyright (c) 2016 Tuukka Turto <tuukka.turto@oktaeder.net>
|
||||||
|
;;
|
||||||
|
;; Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
;; copy of this software and associated documentation files (the "Software"),
|
||||||
|
;; to deal in the Software without restriction, including without limitation
|
||||||
|
;; the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
;; and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
;; Software is furnished to do so, subject to the following conditions:
|
||||||
|
;;
|
||||||
|
;; The above copyright notice and this permission notice shall be included in
|
||||||
|
;; all copies or substantial portions of the Software.
|
||||||
|
;;
|
||||||
|
;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
;; DEALINGS IN THE SOFTWARE.
|
||||||
|
;;
|
||||||
|
|
||||||
|
(defclass Sequence []
|
||||||
|
[--init-- (fn [self func]
|
||||||
|
"initialize a new sequence with a function to compute values"
|
||||||
|
(setv (. self func) func)
|
||||||
|
(setv (. self cache) [])
|
||||||
|
(setv (. self high-water) -1))
|
||||||
|
--getitem-- (fn [self n]
|
||||||
|
"get nth item of sequence"
|
||||||
|
(if (hasattr n "start")
|
||||||
|
(genexpr (get self x) [x (range n.start n.stop
|
||||||
|
(or n.step 1))])
|
||||||
|
(do (when (neg? n)
|
||||||
|
; Call (len) to force the whole
|
||||||
|
; sequence to be evaluated.
|
||||||
|
(len self))
|
||||||
|
(if (<= n (. self high-water))
|
||||||
|
(get (. self cache) n)
|
||||||
|
(do (while (< (. self high-water) n)
|
||||||
|
(setv (. self high-water) (inc (. self high-water)))
|
||||||
|
(.append (. self cache) (.func self (. self high-water))))
|
||||||
|
(get self n))))))
|
||||||
|
--iter-- (fn [self]
|
||||||
|
"create iterator for this sequence"
|
||||||
|
(setv index 0)
|
||||||
|
(try (while True
|
||||||
|
(yield (get self index))
|
||||||
|
(setv index (inc index)))
|
||||||
|
(except [_ IndexError]
|
||||||
|
(raise StopIteration))))
|
||||||
|
--len-- (fn [self]
|
||||||
|
"length of the sequence, dangerous for infinite sequences"
|
||||||
|
(setv index (. self high-water))
|
||||||
|
(try (while True
|
||||||
|
(get self index)
|
||||||
|
(setv index (inc index)))
|
||||||
|
(except [_ IndexError]
|
||||||
|
(len (. self cache)))))
|
||||||
|
max-items-in-repr 10
|
||||||
|
--str-- (fn [self]
|
||||||
|
"string representation of this sequence"
|
||||||
|
(setv items (list (take (inc self.max-items-in-repr) self)))
|
||||||
|
(.format (if (> (len items) self.max-items-in-repr)
|
||||||
|
"[{0}, ...]"
|
||||||
|
"[{0}]")
|
||||||
|
(.join ", " (map str items))))
|
||||||
|
--repr-- (fn [self]
|
||||||
|
"string representation of this sequence"
|
||||||
|
(.--str-- self))])
|
||||||
|
|
||||||
|
(defmacro seq [param &rest seq-code]
|
||||||
|
`(Sequence (fn ~param (do ~@seq-code))))
|
||||||
|
|
||||||
|
(defmacro defseq [seq-name param &rest seq-code]
|
||||||
|
`(def ~seq-name (Sequence (fn ~param (do ~@seq-code)))))
|
||||||
|
|
||||||
|
(defn end-sequence []
|
||||||
|
"raise IndexError exception to signal end of sequence"
|
||||||
|
(raise (IndexError "list index out of range")))
|
@ -21,6 +21,7 @@ from .native_tests.contrib.walk import * # noqa
|
|||||||
from .native_tests.contrib.multi import * # noqa
|
from .native_tests.contrib.multi import * # noqa
|
||||||
from .native_tests.contrib.curry import * # noqa
|
from .native_tests.contrib.curry import * # noqa
|
||||||
from .native_tests.contrib.botsbuildbots import * # noqa
|
from .native_tests.contrib.botsbuildbots import * # noqa
|
||||||
|
from .native_tests.contrib.sequences import * # noqa
|
||||||
|
|
||||||
if PY3:
|
if PY3:
|
||||||
from .native_tests.py3_only_tests import * # noqa
|
from .native_tests.py3_only_tests import * # noqa
|
||||||
|
111
tests/native_tests/contrib/sequences.hy
Normal file
111
tests/native_tests/contrib/sequences.hy
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
;; Copyright (c) 2016 Tuukka Turto <tuukka.turto@oktaeder.net>
|
||||||
|
;;
|
||||||
|
;; Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
;; copy of this software and associated documentation files (the "Software"),
|
||||||
|
;; to deal in the Software without restriction, including without limitation
|
||||||
|
;; the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
;; and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
;; Software is furnished to do so, subject to the following conditions:
|
||||||
|
;;
|
||||||
|
;; The above copyright notice and this permission notice shall be included in
|
||||||
|
;; all copies or substantial portions of the Software.
|
||||||
|
;;
|
||||||
|
;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
;; DEALINGS IN THE SOFTWARE.
|
||||||
|
;;
|
||||||
|
|
||||||
|
(require [hy.contrib.sequences [seq defseq]])
|
||||||
|
|
||||||
|
(import [hy.contrib.sequences [Sequence end-sequence]])
|
||||||
|
|
||||||
|
(defn test-infinite-sequence []
|
||||||
|
"NATIVE: test creating infinite sequence"
|
||||||
|
(assert (= (list (take 5 (seq [n] n)))
|
||||||
|
[0 1 2 3 4])))
|
||||||
|
|
||||||
|
(defn test-indexing-sequence []
|
||||||
|
"NATIVE: test indexing sequence"
|
||||||
|
(defseq shorty [n]
|
||||||
|
(cond [(< n 10) n]
|
||||||
|
[True (end-sequence)]))
|
||||||
|
(setv 0-to-9 (list (range 10)))
|
||||||
|
(assert (= (get shorty 0)
|
||||||
|
(get 0-to-9 0))
|
||||||
|
"getting first element failed")
|
||||||
|
(assert (= (get shorty 5)
|
||||||
|
(get 0-to-9 5))
|
||||||
|
"getting 5th element failed")
|
||||||
|
(assert (= (get shorty -1)
|
||||||
|
(get 0-to-9 -1))
|
||||||
|
"getting element -1 failed"))
|
||||||
|
|
||||||
|
(defn test-slicing-sequence []
|
||||||
|
"NATIVE: test slicing sequence"
|
||||||
|
(defseq shorty [n]
|
||||||
|
(cond [(< n 10) n]
|
||||||
|
[True (end-sequence)]))
|
||||||
|
(setv 0-to-9 (list (range 10)))
|
||||||
|
(assert (= (first shorty)
|
||||||
|
(first 0-to-9))
|
||||||
|
"getting first failed")
|
||||||
|
(assert (= (list (rest shorty))
|
||||||
|
(list (rest 0-to-9)))
|
||||||
|
"getting rest failed")
|
||||||
|
(assert (= (list (cut shorty 2 6))
|
||||||
|
(list (cut 0-to-9 2 6)))
|
||||||
|
"cutting 2-6 failed")
|
||||||
|
(assert (= (list (cut shorty 2 8 2))
|
||||||
|
(list (cut 0-to-9 2 8 2)))
|
||||||
|
"cutting 2-8-2 failed")
|
||||||
|
(assert (= (list (cut shorty 8 2 -2))
|
||||||
|
(list (cut 0-to-9 8 2 -2)))
|
||||||
|
"negative cut failed"))
|
||||||
|
|
||||||
|
(defn test-recursive-sequence []
|
||||||
|
"NATIVE: test defining a recursive sequence"
|
||||||
|
(defseq fibonacci [n]
|
||||||
|
(cond [(= n 0) 0]
|
||||||
|
[(= n 1) 1]
|
||||||
|
[True (+ (get fibonacci (- n 1))
|
||||||
|
(get fibonacci (- n 2)))]))
|
||||||
|
(assert (= (first fibonacci)
|
||||||
|
0)
|
||||||
|
"first element of fibonacci didn't match")
|
||||||
|
(assert (= (second fibonacci)
|
||||||
|
1)
|
||||||
|
"second element of fibonacci didn't match")
|
||||||
|
(assert (= (get fibonacci 40)
|
||||||
|
102334155)
|
||||||
|
"40th element of fibonacci didn't match")
|
||||||
|
(assert (= (list (take 9 fibonacci))
|
||||||
|
[0 1 1 2 3 5 8 13 21])
|
||||||
|
"taking 8 elements of fibonacci didn't match"))
|
||||||
|
|
||||||
|
(defn test-nested-functions []
|
||||||
|
"NATIVE: test that defining nested functions is possible"
|
||||||
|
(defseq primes [n]
|
||||||
|
"infinite sequence of prime numbers"
|
||||||
|
(defn divisible? [n prevs]
|
||||||
|
"is n divisible by any item in prevs?"
|
||||||
|
(any (map (fn [x]
|
||||||
|
(not (% n x)))
|
||||||
|
prevs)))
|
||||||
|
(defn previous-primes [n]
|
||||||
|
"previous prime numbers"
|
||||||
|
(take (dec n) primes))
|
||||||
|
(defn next-possible-prime [n]
|
||||||
|
"next possible prime after nth prime"
|
||||||
|
(inc (get primes (dec n))))
|
||||||
|
(cond [(= n 0) 2]
|
||||||
|
[True (do (setv guess (next-possible-prime n))
|
||||||
|
(while (divisible? guess (previous-primes n))
|
||||||
|
(setv guess (inc guess)))
|
||||||
|
guess)]))
|
||||||
|
(assert (= (list (take 10 primes))
|
||||||
|
[2 3 5 7 11 13 17 19 23 29])
|
||||||
|
"prime sequence didn't match"))
|
Loading…
x
Reference in New Issue
Block a user