Merge branch 'seq'
This commit is contained in:
commit
b3d7069fb3
@ -12,4 +12,5 @@ Contents:
|
||||
flow
|
||||
loop
|
||||
multi
|
||||
sequences
|
||||
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.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
|
||||
|
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…
Reference in New Issue
Block a user