Reimplement some built-ins in terms of the standard library.
As a result: * functions such as `nth` should work correctly on iterators; * `nth` will raise `IndexError` (in a fashion consistent with `get`) when the index is out of bounds; * `take`, etc. will raise `ValueError` instead of returning an ambiguous value if the index is negative; * `map`, `zip`, `range`, `input`, `filter` work the same way (Py3k one) on both Python 2 and 3 (see #523 and #331).
This commit is contained in:
parent
bdd8e3c82e
commit
8e4b21103c
@ -495,8 +495,8 @@ nth
|
|||||||
Usage: ``(nth coll n)``
|
Usage: ``(nth coll n)``
|
||||||
|
|
||||||
Return the `nth` item in a collection, counting from 0. Unlike
|
Return the `nth` item in a collection, counting from 0. Unlike
|
||||||
``get``, ``nth`` works on both iterators and iterables. Returns ``None``
|
``get``, ``nth`` works on both iterators and iterables. Raises ``IndexError``
|
||||||
if the `n` is outside the range of `coll`.
|
if the `n` is outside the range of ``coll`` or ``ValueError`` if it's negative.
|
||||||
|
|
||||||
.. code-block:: hy
|
.. code-block:: hy
|
||||||
|
|
||||||
@ -506,8 +506,10 @@ if the `n` is outside the range of `coll`.
|
|||||||
=> (nth [1 2 4 7] 3)
|
=> (nth [1 2 4 7] 3)
|
||||||
7
|
7
|
||||||
|
|
||||||
=> (none? (nth [1 2 4 7] 5))
|
=> (nth [1 2 4 7] 5)
|
||||||
True
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
IndexError: 5
|
||||||
|
|
||||||
=> (nth (take 3 (drop 2 [1 2 3 4 5 6])) 2))
|
=> (nth (take 3 (drop 2 [1 2 3 4 5 6])) 2))
|
||||||
5
|
5
|
||||||
@ -764,6 +766,7 @@ drop
|
|||||||
Usage: ``(drop n coll)``
|
Usage: ``(drop n coll)``
|
||||||
|
|
||||||
Return an iterator, skipping the first ``n`` members of ``coll``
|
Return an iterator, skipping the first ``n`` members of ``coll``
|
||||||
|
Raises ``ValueError`` if ``n`` is negative.
|
||||||
|
|
||||||
.. code-block:: hy
|
.. code-block:: hy
|
||||||
|
|
||||||
@ -924,6 +927,7 @@ take
|
|||||||
Usage: ``(take n coll)``
|
Usage: ``(take n coll)``
|
||||||
|
|
||||||
Return an iterator containing the first ``n`` members of ``coll``.
|
Return an iterator containing the first ``n`` members of ``coll``.
|
||||||
|
Raises ``ValueError`` if ``n`` is negative.
|
||||||
|
|
||||||
.. code-block:: hy
|
.. code-block:: hy
|
||||||
|
|
||||||
|
@ -23,7 +23,9 @@
|
|||||||
;;;; to make functional programming slightly easier.
|
;;;; to make functional programming slightly easier.
|
||||||
;;;;
|
;;;;
|
||||||
|
|
||||||
|
(import itertools)
|
||||||
|
(import functools)
|
||||||
|
(import collections)
|
||||||
(import [hy._compat [long-type]]) ; long for python2, int for python3
|
(import [hy._compat [long-type]]) ; long for python2, int for python3
|
||||||
(import [hy.models.cons [HyCons]])
|
(import [hy.models.cons [HyCons]])
|
||||||
|
|
||||||
@ -49,15 +51,6 @@
|
|||||||
(and (instance? (type :foo) k)
|
(and (instance? (type :foo) k)
|
||||||
(.startswith k (get :foo 0))))
|
(.startswith k (get :foo 0))))
|
||||||
|
|
||||||
(defn cycle [coll]
|
|
||||||
"Yield an infinite repetition of the items in coll"
|
|
||||||
(setv seen [])
|
|
||||||
(for* [x coll]
|
|
||||||
(yield x)
|
|
||||||
(.append seen x))
|
|
||||||
(while seen
|
|
||||||
(for* [x seen]
|
|
||||||
(yield x))))
|
|
||||||
|
|
||||||
(defn dec [n]
|
(defn dec [n]
|
||||||
"Decrement n by 1"
|
"Decrement n by 1"
|
||||||
@ -87,22 +80,36 @@
|
|||||||
(yield val)
|
(yield val)
|
||||||
(.add seen val))))))
|
(.add seen val))))))
|
||||||
|
|
||||||
|
(if-python2
|
||||||
|
(do
|
||||||
|
(setv filterfalse itertools.ifilterfalse)
|
||||||
|
(setv zip_longest itertools.izip_longest)
|
||||||
|
(setv filter itertools.ifilter)
|
||||||
|
(setv map itertools.imap)
|
||||||
|
(setv zip itertools.izip)
|
||||||
|
(setv range xrange)
|
||||||
|
(setv input raw_input))
|
||||||
|
(do
|
||||||
|
(setv reduce functools.reduce)
|
||||||
|
(setv filterfalse itertools.filterfalse)
|
||||||
|
(setv zip_longest itertools.zip_longest)
|
||||||
|
; Someone can import these directly from `hy.core.language`;
|
||||||
|
; we'll make some duplicates.
|
||||||
|
(setv filter filter)
|
||||||
|
(setv map map)
|
||||||
|
(setv zip zip)
|
||||||
|
(setv range range)
|
||||||
|
(setv input input)))
|
||||||
|
|
||||||
|
(setv cycle itertools.cycle)
|
||||||
|
(setv repeat itertools.repeat)
|
||||||
|
(setv drop-while itertools.dropwhile)
|
||||||
|
(setv take-while itertools.takewhile)
|
||||||
|
(setv zipwith map)
|
||||||
|
|
||||||
(defn drop [count coll]
|
(defn drop [count coll]
|
||||||
"Drop `count` elements from `coll` and yield back the rest"
|
"Drop `count` elements from `coll` and yield back the rest"
|
||||||
(let [[citer (iter coll)]]
|
(itertools.islice coll count nil))
|
||||||
(try (for* [i (range count)]
|
|
||||||
(next citer))
|
|
||||||
(catch [StopIteration]))
|
|
||||||
citer))
|
|
||||||
|
|
||||||
(defn drop-while [pred coll]
|
|
||||||
"Drop all elements of `coll` until `pred` is False"
|
|
||||||
(let [[citer (iter coll)]]
|
|
||||||
(for* [val citer]
|
|
||||||
(if (not (pred val))
|
|
||||||
(do (yield val) (break))))
|
|
||||||
(for* [val citer]
|
|
||||||
(yield val))))
|
|
||||||
|
|
||||||
(defn empty? [coll]
|
(defn empty? [coll]
|
||||||
"Return True if `coll` is empty"
|
"Return True if `coll` is empty"
|
||||||
@ -126,13 +133,6 @@
|
|||||||
(if (not (hasattr tree attr))
|
(if (not (hasattr tree attr))
|
||||||
(setattr tree attr 1))))
|
(setattr tree attr 1))))
|
||||||
|
|
||||||
(defn filter [pred coll]
|
|
||||||
"Return all elements from `coll` that pass `pred`"
|
|
||||||
(let [[citer (iter coll)]]
|
|
||||||
(for* [val citer]
|
|
||||||
(if (pred val)
|
|
||||||
(yield val)))))
|
|
||||||
|
|
||||||
(defn flatten [coll]
|
(defn flatten [coll]
|
||||||
"Return a single flat list expanding all members of coll"
|
"Return a single flat list expanding all members of coll"
|
||||||
(if (coll? coll)
|
(if (coll? coll)
|
||||||
@ -174,7 +174,7 @@
|
|||||||
|
|
||||||
(defn first [coll]
|
(defn first [coll]
|
||||||
"Return first item from `coll`"
|
"Return first item from `coll`"
|
||||||
(get coll 0))
|
(nth coll 0))
|
||||||
|
|
||||||
(defn identity [x]
|
(defn identity [x]
|
||||||
"Returns the argument unchanged"
|
"Returns the argument unchanged"
|
||||||
@ -205,8 +205,7 @@
|
|||||||
|
|
||||||
(defn iterable? [x]
|
(defn iterable? [x]
|
||||||
"Return true if x is iterable"
|
"Return true if x is iterable"
|
||||||
(try (do (iter x) true)
|
(isinstance x collections.Iterable))
|
||||||
(catch [Exception] false)))
|
|
||||||
|
|
||||||
(defn iterate [f x]
|
(defn iterate [f x]
|
||||||
(setv val x)
|
(setv val x)
|
||||||
@ -216,8 +215,7 @@
|
|||||||
|
|
||||||
(defn iterator? [x]
|
(defn iterator? [x]
|
||||||
"Return true if x is an iterator"
|
"Return true if x is an iterator"
|
||||||
(try (= x (iter x))
|
(isinstance x collections.Iterator))
|
||||||
(catch [TypeError] false)))
|
|
||||||
|
|
||||||
(defn list* [hd &rest tl]
|
(defn list* [hd &rest tl]
|
||||||
"Return a dotted list construed from the elements of the argument"
|
"Return a dotted list construed from the elements of the argument"
|
||||||
@ -258,13 +256,9 @@
|
|||||||
|
|
||||||
(defn nth [coll index]
|
(defn nth [coll index]
|
||||||
"Return nth item in collection or sequence, counting from 0"
|
"Return nth item in collection or sequence, counting from 0"
|
||||||
(if (not (neg? index))
|
(try
|
||||||
(if (iterable? coll)
|
(next (drop index coll))
|
||||||
(try (get (list (take 1 (drop index coll))) 0)
|
(catch [e StopIteration] (raise (IndexError index)))))
|
||||||
(catch [IndexError] None))
|
|
||||||
(try (get coll index)
|
|
||||||
(catch [IndexError] None)))
|
|
||||||
None))
|
|
||||||
|
|
||||||
(defn odd? [n]
|
(defn odd? [n]
|
||||||
"Return true if n is an odd number"
|
"Return true if n is an odd number"
|
||||||
@ -285,14 +279,7 @@
|
|||||||
|
|
||||||
(defn rest [coll]
|
(defn rest [coll]
|
||||||
"Get all the elements of a coll, except the first."
|
"Get all the elements of a coll, except the first."
|
||||||
(slice coll 1))
|
(drop 1 coll))
|
||||||
|
|
||||||
(defn repeat [x &optional n]
|
|
||||||
"Yield x forever or optionally n times"
|
|
||||||
(if (none? n)
|
|
||||||
(setv dispatch (fn [] (while true (yield x))))
|
|
||||||
(setv dispatch (fn [] (for* [_ (range n)] (yield x)))))
|
|
||||||
(dispatch))
|
|
||||||
|
|
||||||
(defn repeatedly [func]
|
(defn repeatedly [func]
|
||||||
"Yield result of running func repeatedly"
|
"Yield result of running func repeatedly"
|
||||||
@ -301,7 +288,7 @@
|
|||||||
|
|
||||||
(defn second [coll]
|
(defn second [coll]
|
||||||
"Return second item from `coll`"
|
"Return second item from `coll`"
|
||||||
(get coll 1))
|
(nth coll 1))
|
||||||
|
|
||||||
(defn some [pred coll]
|
(defn some [pred coll]
|
||||||
"Return true if (pred x) is logical true for any x in coll, else false"
|
"Return true if (pred x) is logical true for any x in coll, else false"
|
||||||
@ -322,9 +309,7 @@
|
|||||||
(defn take [count coll]
|
(defn take [count coll]
|
||||||
"Take `count` elements from `coll`, or the whole set if the total
|
"Take `count` elements from `coll`, or the whole set if the total
|
||||||
number of entries in `coll` is less than `count`."
|
number of entries in `coll` is less than `count`."
|
||||||
(let [[citer (iter coll)]]
|
(itertools.islice coll nil count))
|
||||||
(for* [_ (range count)]
|
|
||||||
(yield (next citer)))))
|
|
||||||
|
|
||||||
(defn take-nth [n coll]
|
(defn take-nth [n coll]
|
||||||
"Return every nth member of coll
|
"Return every nth member of coll
|
||||||
@ -337,29 +322,15 @@
|
|||||||
(next citer))))
|
(next citer))))
|
||||||
(raise (ValueError "n must be positive"))))
|
(raise (ValueError "n must be positive"))))
|
||||||
|
|
||||||
(defn take-while [pred coll]
|
|
||||||
"Take all elements while `pred` is true"
|
|
||||||
(let [[citer (iter coll)]]
|
|
||||||
(for* [val citer]
|
|
||||||
(if (pred val)
|
|
||||||
(yield val)
|
|
||||||
(break)))))
|
|
||||||
|
|
||||||
(defn zero? [n]
|
(defn zero? [n]
|
||||||
"Return true if n is 0"
|
"Return true if n is 0"
|
||||||
(_numeric_check n)
|
(_numeric_check n)
|
||||||
(= n 0))
|
(= n 0))
|
||||||
|
|
||||||
(defn zipwith [func &rest lists]
|
|
||||||
"Zip the contents of several lists and map a function to the result"
|
|
||||||
(do
|
|
||||||
(import functools)
|
|
||||||
(map (functools.partial (fn [f args] (apply f args)) func) (apply zip lists))))
|
|
||||||
|
|
||||||
(def *exports* '[calling-module-name coll? cons cons? cycle dec distinct
|
(def *exports* '[calling-module-name coll? cons cons? cycle dec distinct
|
||||||
disassemble drop drop-while empty? even? every? first filter
|
disassemble drop drop-while empty? even? every? first filter
|
||||||
flatten float? gensym identity inc instance? integer
|
flatten float? gensym identity inc instance? integer
|
||||||
integer? integer-char? iterable? iterate iterator? keyword?
|
integer? integer-char? iterable? iterate iterator? keyword?
|
||||||
list* macroexpand macroexpand-1 neg? nil? none? nth
|
list* macroexpand macroexpand-1 map neg? nil? none? nth
|
||||||
numeric? odd? pos? remove repeat repeatedly rest second
|
numeric? odd? pos? range remove repeat repeatedly rest second
|
||||||
some string string? take take-nth take-while zero? zipwith])
|
some string string? take take-nth take-while zero? zip zipwith])
|
||||||
|
@ -82,8 +82,8 @@
|
|||||||
(assert-equal res [None 4 5])
|
(assert-equal res [None 4 5])
|
||||||
(setv res (list (drop 0 [1 2 3 4 5])))
|
(setv res (list (drop 0 [1 2 3 4 5])))
|
||||||
(assert-equal res [1 2 3 4 5])
|
(assert-equal res [1 2 3 4 5])
|
||||||
(setv res (list (drop -1 [1 2 3 4 5])))
|
(try (do (list (drop -1 [1 2 3 4 5])) (assert False))
|
||||||
(assert-equal res [1 2 3 4 5])
|
(catch [e [ValueError]] nil))
|
||||||
(setv res (list (drop 6 (iter [1 2 3 4 5]))))
|
(setv res (list (drop 6 (iter [1 2 3 4 5]))))
|
||||||
(assert-equal res [])
|
(assert-equal res [])
|
||||||
(setv res (list (take 5 (drop 2 (iterate inc 0)))))
|
(setv res (list (take 5 (drop 2 (iterate inc 0)))))
|
||||||
@ -335,12 +335,15 @@
|
|||||||
"NATIVE: testing the nth function"
|
"NATIVE: testing the nth function"
|
||||||
(assert-equal 2 (nth [1 2 4 7] 1))
|
(assert-equal 2 (nth [1 2 4 7] 1))
|
||||||
(assert-equal 7 (nth [1 2 4 7] 3))
|
(assert-equal 7 (nth [1 2 4 7] 3))
|
||||||
(assert-true (none? (nth [1 2 4 7] 5)))
|
(try (do (nth [1 2 4 7] 5) (assert False))
|
||||||
(assert-true (none? (nth [1 2 4 7] -1)))
|
(catch [e [IndexError]] nil))
|
||||||
|
(try (do (nth [1 2 4 7] -1) (assert False))
|
||||||
|
(catch [e [ValueError]] nil))
|
||||||
;; now for iterators
|
;; now for iterators
|
||||||
(assert-equal 2 (nth (iter [1 2 4 7]) 1))
|
(assert-equal 2 (nth (iter [1 2 4 7]) 1))
|
||||||
(assert-equal 7 (nth (iter [1 2 4 7]) 3))
|
(assert-equal 7 (nth (iter [1 2 4 7]) 3))
|
||||||
(assert-true (none? (nth (iter [1 2 4 7]) -1)))
|
(try (do (nth (iter [1 2 4 7]) -1) (assert False))
|
||||||
|
(catch [e [ValueError]] nil))
|
||||||
(assert-equal 5 (nth (take 3 (drop 2 [1 2 3 4 5 6])) 2)))
|
(assert-equal 5 (nth (take 3 (drop 2 [1 2 3 4 5 6])) 2)))
|
||||||
|
|
||||||
(defn test-numeric? []
|
(defn test-numeric? []
|
||||||
@ -429,8 +432,8 @@
|
|||||||
(assert-equal res ["s" "s" "s" "s"])
|
(assert-equal res ["s" "s" "s" "s"])
|
||||||
(setv res (list (take 0 (repeat "s"))))
|
(setv res (list (take 0 (repeat "s"))))
|
||||||
(assert-equal res [])
|
(assert-equal res [])
|
||||||
(setv res (list (take -1 (repeat "s"))))
|
(try (do (list (take -1 (repeat "s"))) (assert False))
|
||||||
(assert-equal res [])
|
(catch [e [ValueError]] nil))
|
||||||
(setv res (list (take 6 [1 2 None 4])))
|
(setv res (list (take 6 [1 2 None 4])))
|
||||||
(assert-equal res [1 2 None 4]))
|
(assert-equal res [1 2 None 4]))
|
||||||
|
|
||||||
|
@ -474,7 +474,7 @@
|
|||||||
|
|
||||||
(defn test-rest []
|
(defn test-rest []
|
||||||
"NATIVE: test rest"
|
"NATIVE: test rest"
|
||||||
(assert (= (rest [1 2 3 4 5]) [2 3 4 5])))
|
(assert (= (list (rest [1 2 3 4 5])) [2 3 4 5])))
|
||||||
|
|
||||||
|
|
||||||
(defn test-importas []
|
(defn test-importas []
|
||||||
|
Loading…
x
Reference in New Issue
Block a user