Add lazy sequences into contrib
This commit is contained in:
parent
2242097b6b
commit
241d554b0b
@ -12,4 +12,5 @@ Contents:
|
||||
flow
|
||||
loop
|
||||
multi
|
||||
sequences
|
||||
walk
|
||||
|
77
docs/contrib/sequences.rst
Normal file
77
docs/contrib/sequences.rst
Normal file
@ -0,0 +1,77 @@
|
||||
==============
|
||||
Lazy sequences
|
||||
==============
|
||||
|
||||
.. versionadded:: 0.12.0
|
||||
|
||||
Sequences module contains few macros for declaring sequences that are evaluated
|
||||
only as much as the client code requests elements. Compared to generators they
|
||||
allow accessing same element multiple times. Since they cache calculated
|
||||
values, they aren't suited for infinite sequences. However, the implementation
|
||||
allows recursive definition of sequences, without resulting recursive
|
||||
computation.
|
||||
|
||||
To use these macros you need to require them and import other types like:
|
||||
|
||||
.. code-block:: hy
|
||||
|
||||
(require [hy.contrib.sequences [defseq seq]])
|
||||
(import [hy.contrib.sequences [Sequence end-sequence]])
|
||||
|
||||
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, ``end-sequence`` needs to be called to signal end of
|
||||
the sequence:
|
||||
|
||||
.. code-block:: hy
|
||||
|
||||
(seq [n]
|
||||
(cond [(< n 5) n]
|
||||
[true (end-sequence)]))
|
||||
|
||||
This creates following sequence: ``[0 1 2 3 4]``. For such a sequence ``len``
|
||||
returns amount of items in sequence and negative indexing is suported. Because
|
||||
both of thse require evaluating whole sequence, calling such a function would
|
||||
take forever (or at least until available memory has been exhausted).
|
||||
|
||||
Sequence can be defined recursively. Canonical example of fibonacci numbers
|
||||
is defined as:
|
||||
|
||||
.. code-block:: hy
|
||||
|
||||
(defseq fibonacci [n]
|
||||
(cond [(= n 0) 0]
|
||||
[(= n 1) 1]
|
||||
[true (+ (get fibonacci (- n 1))
|
||||
(get fibonacci (- n 2)))]))
|
||||
|
||||
This results sequence of ``[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 end of a sequence when iterator reaches certain point of sequence.
|
||||
Internally this is done by raising ``IndexError``, catching that in iterator
|
||||
and raising ``StopIteration``.
|
78
hy/contrib/sequences.hy
Normal file
78
hy/contrib/sequences.hy
Normal file
@ -0,0 +1,78 @@
|
||||
;; 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)
|
||||
nil)
|
||||
--getitem-- (fn [self n]
|
||||
"get nth item of sequence"
|
||||
(if (hasattr n "start")
|
||||
(if n.step
|
||||
(genexpr (get self x) [x (range n.start n.stop n.step)])
|
||||
(genexpr (get self x) [x (range n.start n.stop 1)]))
|
||||
(do (when (neg? n)
|
||||
(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)))))
|
||||
--str-- (fn [self]
|
||||
"string representation of this sequence"
|
||||
(setv items (list (take 11 self)))
|
||||
(.format (if (= (len items) 11)
|
||||
"[{0}, ...]"
|
||||
"[{0}]")
|
||||
(.join ", " (map str items))))
|
||||
--repr-- (fn [self]
|
||||
"string representation of this sequence"
|
||||
(.--str-- self))])
|
||||
|
||||
(defmacro seq [param seq-code]
|
||||
`(Sequence (fn ~param ~seq-code)))
|
||||
|
||||
(defmacro defseq [seq-name param seq-code]
|
||||
`(def ~seq-name (seq ~param ~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.curry import * # noqa
|
||||
from .native_tests.contrib.botsbuildbots import * # noqa
|
||||
from .native_tests.contrib.sequences import * # noqa
|
||||
|
||||
if PY3:
|
||||
from .native_tests.py3_only_tests import * # noqa
|
||||
|
85
tests/native_tests/contrib/sequences.hy
Normal file
85
tests/native_tests/contrib/sequences.hy
Normal file
@ -0,0 +1,85 @@
|
||||
;; 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)]))
|
||||
(assert (= (get shorty 0)
|
||||
(get (range 10) 0))
|
||||
"getting first element failed")
|
||||
(assert (= (get shorty 5)
|
||||
(get (range 10) 5))
|
||||
"getting 5th element failed")
|
||||
(assert (= (get shorty -1)
|
||||
(get (range 10) -1))
|
||||
"getting element -1 failed"))
|
||||
|
||||
(defn test-slicing-sequence []
|
||||
"NATIVE: test slicing sequence"
|
||||
(defseq shorty [n]
|
||||
(cond [(< n 10) n]
|
||||
[true (end-sequence)]))
|
||||
(assert (= (first shorty)
|
||||
(first (range 10)))
|
||||
"getting first failed")
|
||||
(assert (= (list (rest shorty))
|
||||
(list (rest (range 10))))
|
||||
"getting rest failed")
|
||||
(assert (= (list (cut shorty 2 6))
|
||||
(list (cut (range 10) 2 6)))
|
||||
"cutting 2-6 failed")
|
||||
(assert (= (list (cut shorty 2 8 2))
|
||||
(list (cut (range 10) 2 8 2)))
|
||||
"cutting 2-8-2 failed")
|
||||
(assert (= (list (cut shorty 8 2 -2))
|
||||
(list (cut (range 10) 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"))
|
Loading…
x
Reference in New Issue
Block a user