Updated most methods to replace While with For, and added tons of new tests
    for things like (cycle []) and lists with None's in them.

    thanks @olasd

Add set of new core functions

Add set of new core functions to the stdlib.

Moved the auto-import code from compile_expression to
HySymbol so that "even?' in this style expression will
be found and imported.

(list (filter even? [1 2 3 4 5]))

The core functions are documented in 2 sections, one
for basic functions like (even?..) and (nth ...) and
one for all the sequence functions.

Update: This removes all the caching decorators, misnamed as
'lazy-seq' from the core. All sequence methods now just use
yield to return a generator, so they are Python-lazy

Further refinements of core functions

Cleaned up the docs to use 'iterator' instead of 'generator'

Fixed drop to just return the iterator instead of an extra
yield loop. But also added a test to catch dropping too
many.
This commit is contained in:
Bob Tolbert 2013-07-07 20:36:26 -06:00 committed by Bob Tolbert
parent e138af8b94
commit 41ae4f4d2c
2 changed files with 133 additions and 57 deletions

View File

@ -3,33 +3,38 @@
;;;; ;;;;
(defn _numeric-check [x]
(if (not (numeric? x))
(raise (TypeError (.format "{0!r} is not a number" x)))))
(defn cycle [coll] (defn cycle [coll]
"Yield an infinite repetition of the items in coll" "Yield an infinite repetition of the items in coll"
(while true (setv seen [])
(for [x coll] (for [x coll]
(yield x)
(.append seen x))
(while seen
(for [x seen]
(yield x)))) (yield x))))
(defn dec [n] (defn dec [n]
"Decrement n by 1" "Decrement n by 1"
(_numeric-check n)
(- n 1)) (- n 1))
(defn distinct [coll] (defn distinct [coll]
"Return a generator from the original collection with duplicates "Return a generator from the original collection with duplicates
removed" removed"
(let [ [seen [] ] (let [[seen []] [citer (iter coll)]]
[citer (iter coll)] (for [val citer]
[val (next citer)] ]
(while (not (none? val))
(do
(if (not_in val seen) (if (not_in val seen)
(do (do
(yield val) (yield val)
(.append seen val))) (.append seen val))))))
(setv val (next citer))))))
(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)] ] (let [[citer (iter coll)]]
(try (for [i (range count)] (try (for [i (range count)]
(next citer)) (next citer))
(catch [StopIteration])) (catch [StopIteration]))
@ -37,18 +42,19 @@
(defn even? [n] (defn even? [n]
"Return true if n is an even number" "Return true if n is an even number"
(_numeric-check n)
(= (% n 2) 0)) (= (% n 2) 0))
(defn filter [pred coll] (defn filter [pred coll]
"Return all elements from coll that pass filter" "Return all elements from `coll` that pass `pred`"
(let [ [citer (iter coll)] [val (next citer)] ] (let [[citer (iter coll)]]
(while (not (none? val)) (for [val citer]
(if (pred val) (if (pred val)
(yield val)) (yield val)))))
(setv val (next citer)))))
(defn inc [n] (defn inc [n]
"Increment n by 1" "Increment n by 1"
(_numeric-check n)
(+ n 1)) (+ n 1))
(defn instance? [klass x] (defn instance? [klass x]
@ -72,12 +78,17 @@
(defn neg? [n] (defn neg? [n]
"Return true if n is < 0" "Return true if n is < 0"
(_numeric-check n)
(< n 0)) (< n 0))
(defn none? [x] (defn none? [x]
"Return true if x is None" "Return true if x is None"
(is x None)) (is x None))
(defn numeric? [x]
(import numbers)
(instance? numbers.Number x))
(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)) (if (not (neg? index))
@ -90,19 +101,20 @@
(defn odd? [n] (defn odd? [n]
"Return true if n is an odd number" "Return true if n is an odd number"
(_numeric-check n)
(= (% n 2) 1)) (= (% n 2) 1))
(defn pos? [n] (defn pos? [n]
"Return true if n is > 0" "Return true if n is > 0"
(_numeric_check n)
(> n 0)) (> n 0))
(defn remove [pred coll] (defn remove [pred coll]
"Return coll with elements removed that pass `pred`" "Return coll with elements removed that pass `pred`"
(let [ [citer (iter coll)] [val (next citer)] ] (let [[citer (iter coll)]]
(while (not (none? val)) (for [val citer]
(if (not (pred val)) (if (not (pred val))
(yield val)) (yield val)))))
(setv val (next citer)))))
(defn repeat [x &optional n] (defn repeat [x &optional n]
"Yield x forever or optionally n times" "Yield x forever or optionally n times"
@ -116,33 +128,33 @@
(while true (while true
(yield (func)))) (yield (func))))
(defn take [count what] (defn take [count coll]
"Take `count` elements from `what`, or the whole set if the total "Take `count` elements from `coll`, or the whole set if the total
number of entries in `what` is less than `count`." number of entries in `coll` is less than `count`."
(setv what (iter what)) (let [[citer (iter coll)]]
(for [i (range count)] (for [_ (range count)]
(yield (next what)))) (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
raises ValueError for (not (pos? n))" raises ValueError for (not (pos? n))"
(if (pos? n) (if (pos? n)
(let [ [citer (iter coll)] [val (next citer)] ] (let [[citer (iter coll)] [skip (dec n)]]
(while (not (none? val)) (for [val citer]
(yield val) (yield val)
(for [_ (range (dec n))] (for [_ (range skip)]
(next citer)) (next citer))))
(setv val (next citer))))
(raise (ValueError "n must be positive")))) (raise (ValueError "n must be positive"))))
(defn take-while [pred coll] (defn take-while [pred coll]
"Take all elements while `pred` is true" "Take all elements while `pred` is true"
(let [ [citer (iter coll)] [val (next citer)] ] (let [[citer (iter coll)]]
(while (pred val) (for [val citer]
(if (pred val)
(yield val) (yield val)
(setv val (next citer))))) (break)))))
(def *exports* ["cycle" "dec" "distinct" "drop" "even?" "filter" "inc" (def *exports* ["cycle" "dec" "distinct" "drop" "even?" "filter" "inc"
"instance?" "iterable?" "iterate" "iterator?" "neg?" "instance?" "iterable?" "iterate" "iterator?" "neg?"
"none?" "nth" "odd?" "pos?" "remove" "repeat" "repeatedly" "none?" "nth" "numeric?" "odd?" "pos?" "remove" "repeat"
"take" "take_nth" "take_while"]) "repeatedly" "take" "take_nth" "take_while"])

View File

@ -13,14 +13,22 @@
(defn test-cycle [] (defn test-cycle []
"NATIVE: testing cycle" "NATIVE: testing cycle"
(assert-equal (list (cycle [])) [])
(assert-equal (list (take 7 (cycle [1 2 3]))) [1 2 3 1 2 3 1]) (assert-equal (list (take 7 (cycle [1 2 3]))) [1 2 3 1 2 3 1])
(assert-equal (list (take 2 (cycle [1 2 3]))) [1 2])) (assert-equal (list (take 2 (cycle [1 2 3]))) [1 2])
(assert-equal (list (take 4 (cycle [1 None 3]))) [1 None 3 1]))
(defn test-dec [] (defn test-dec []
"NATIVE: testing the dec function" "NATIVE: testing the dec function"
(assert-equal 0 (dec 1)) (assert-equal 0 (dec 1))
(assert-equal -1 (dec 0)) (assert-equal -1 (dec 0))
(assert-equal 0 (dec (dec 2)))) (assert-equal 0 (dec (dec 2)))
(try (do (dec "foo") (assert False))
(catch [e [TypeError]] (assert (in "not a number" (str e)))))
(try (do (dec []) (assert False))
(catch [e [TypeError]] (assert (in "not a number" (str e)))))
(try (do (dec None) (assert False))
(catch [e [TypeError]] (assert (in "not a number" (str e))))))
(defn test-distinct [] (defn test-distinct []
"NATIVE: testing the distinct function" "NATIVE: testing the distinct function"
@ -32,14 +40,19 @@
;; now with an iter ;; now with an iter
(setv test_iter (iter [1 2 3 4 3 5 2])) (setv test_iter (iter [1 2 3 4 3 5 2]))
(setv res (list (distinct test_iter))) (setv res (list (distinct test_iter)))
(assert-equal res [1 2 3 4 5])) (assert-equal res [1 2 3 4 5])
; make sure we can handle None in the list
(setv res (list (distinct [1 2 3 2 5 None 3 4 None])))
(assert-equal res [1 2 3 5 None 4]))
(defn test-drop [] (defn test-drop []
"NATIVE: testing drop function" "NATIVE: testing drop function"
(setv res (list (drop 2 [1 2 3 4 5]))) (setv res (list (drop 2 [1 2 3 4 5])))
(assert-equal res [ 3 4 5]) (assert-equal res [3 4 5])
(setv res (list (drop 3 (iter [1 2 3 4 5])))) (setv res (list (drop 3 (iter [1 2 3 4 5]))))
(assert-equal res [ 4 5]) (assert-equal res [4 5])
(setv res (list (drop 3 (iter [1 2 3 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]))) (setv res (list (drop -1 [1 2 3 4 5])))
@ -53,7 +66,13 @@
"NATIVE: testing the even? function" "NATIVE: testing the even? function"
(assert-true (even? -2)) (assert-true (even? -2))
(assert-false (even? 1)) (assert-false (even? 1))
(assert-true (even? 0))) (assert-true (even? 0))
(try (even? "foo")
(catch [e [TypeError]] (assert (in "not a number" (str e)))))
(try (even? [])
(catch [e [TypeError]] (assert (in "not a number" (str e)))))
(try (even? None)
(catch [e [TypeError]] (assert (in "not a number" (str e))))))
(defn test-filter [] (defn test-filter []
"NATIVE: testing the filter function" "NATIVE: testing the filter function"
@ -63,12 +82,26 @@
(setv res (list (filter pos? (iter [ 1 2 3 -4 5 -6])))) (setv res (list (filter pos? (iter [ 1 2 3 -4 5 -6]))))
(assert-equal res [ 1 2 3 5]) (assert-equal res [ 1 2 3 5])
(setv res (list (filter neg? [ -1 -4 5 3 4]))) (setv res (list (filter neg? [ -1 -4 5 3 4])))
(assert-false (= res [1 2]))) (assert-false (= res [1 2]))
;; test with empty list
(setv res (list (filter neg? [])))
(assert-equal res [])
;; test with None in the list
(setv res (list (filter even? (filter numeric? [1 2 None 3 4 None 4 6]))))
(assert-equal res [2 4 4 6])
(setv res (list (filter none? [1 2 None 3 4 None 4 6])))
(assert-equal res [None None]))
(defn test-inc [] (defn test-inc []
"NATIVE: testing the inc function" "NATIVE: testing the inc function"
(assert-equal 3 (inc 2)) (assert-equal 3 (inc 2))
(assert-equal 0 (inc -1))) (assert-equal 0 (inc -1))
(try (do (inc "foo") (assert False))
(catch [e [TypeError]] (assert (in "not a number" (str e)))))
(try (do (inc []) (assert False))
(catch [e [TypeError]] (assert (in "not a number" (str e)))))
(try (do (inc None) (assert False))
(catch [e [TypeError]] (assert (in "not a number" (str e))))))
(defn test-instance [] (defn test-instance []
"NATIVE: testing instance? function" "NATIVE: testing instance? function"
@ -140,7 +173,13 @@
"NATIVE: testing the neg? function" "NATIVE: testing the neg? function"
(assert-true (neg? -2)) (assert-true (neg? -2))
(assert-false (neg? 1)) (assert-false (neg? 1))
(assert-false (neg? 0))) (assert-false (neg? 0))
(try (do (neg? "foo") (assert False))
(catch [e [TypeError]] (assert (in "not a number" (str e)))))
(try (do (neg? []) (assert False))
(catch [e [TypeError]] (assert (in "not a number" (str e)))))
(try (do (neg? None) (assert False))
(catch [e [TypeError]] (assert (in "not a number" (str e))))))
(defn test-none [] (defn test-none []
"NATIVE: testing for `is None`" "NATIVE: testing for `is None`"
@ -166,13 +205,25 @@
"NATIVE: testing the odd? function" "NATIVE: testing the odd? function"
(assert-true (odd? -3)) (assert-true (odd? -3))
(assert-true (odd? 1)) (assert-true (odd? 1))
(assert-false (odd? 0))) (assert-false (odd? 0))
(try (do (odd? "foo") (assert False))
(catch [e [TypeError]] (assert (in "not a number" (str e)))))
(try (do (odd? []) (assert False))
(catch [e [TypeError]] (assert (in "not a number" (str e)))))
(try (do (odd? None) (assert False))
(catch [e [TypeError]] (assert (in "not a number" (str e))))))
(defn test-pos [] (defn test-pos []
"NATIVE: testing the pos? function" "NATIVE: testing the pos? function"
(assert-true (pos? 2)) (assert-true (pos? 2))
(assert-false (pos? -1)) (assert-false (pos? -1))
(assert-false (pos? 0))) (assert-false (pos? 0))
(try (do (pos? "foo") (assert False))
(catch [e [TypeError]] (assert (in "not a number" (str e)))))
(try (do (pos? []) (assert False))
(catch [e [TypeError]] (assert (in "not a number" (str e)))))
(try (do (pos? None) (assert False))
(catch [e [TypeError]] (assert (in "not a number" (str e))))))
(defn test-remove [] (defn test-remove []
"NATIVE: testing the remove function" "NATIVE: testing the remove function"
@ -180,7 +231,9 @@
(assert-equal r [2 4 6]) (assert-equal r [2 4 6])
(assert-equal (list (remove even? [1 2 3 4 5])) [1 3 5]) (assert-equal (list (remove even? [1 2 3 4 5])) [1 3 5])
(assert-equal (list (remove neg? [1 2 3 4 5])) [1 2 3 4 5]) (assert-equal (list (remove neg? [1 2 3 4 5])) [1 2 3 4 5])
(assert-equal (list (remove pos? [1 2 3 4 5])) [])) (assert-equal (list (remove pos? [1 2 3 4 5])) [])
;; deal with embedded None
(assert-equal (list (remove (fn [x] (not (numeric? x))) [1 2 None 3 None 4])) [1 2 3 4]))
(defn test-repeat [] (defn test-repeat []
"NATIVE: testing repeat" "NATIVE: testing repeat"
@ -206,7 +259,9 @@
(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")))) (setv res (list (take -1 (repeat "s"))))
(assert-equal res [])) (assert-equal res [])
(setv res (list (take 6 [1 2 None 4])))
(assert-equal res [1 2 None 4]))
(defn test-take-nth [] (defn test-take-nth []
"NATIVE: testing the take-nth function" "NATIVE: testing the take-nth function"
@ -222,6 +277,11 @@
(assert-equal res [1 7]) (assert-equal res [1 7])
(setv res (list (take-nth 7 [1 2 3 4 5 6 7]))) (setv res (list (take-nth 7 [1 2 3 4 5 6 7])))
(assert-equal res [1]) (assert-equal res [1])
;; what if there are None's in list
(setv res (list (take-nth 2 [1 2 3 None 5 6])))
(assert-equal res [1 3 5])
(setv res (list (take-nth 3 [1 2 3 None 5 6])))
(assert-equal res [1 None])
;; using 0 should raise ValueError ;; using 0 should raise ValueError
(let [[passed false]] (let [[passed false]]
(try (try
@ -232,6 +292,10 @@
(defn test-take-while [] (defn test-take-while []
"NATIVE: testing the take-while function" "NATIVE: testing the take-while function"
(setv res (list (take-while pos? [ 1 2 3 -4 5]))) (setv res (list (take-while pos? [ 1 2 3 -4 5])))
(assert-equal res [ 1 2 3 ]) (assert-equal res [1 2 3])
(setv res (list (take-while neg? [ -1 -4 5 3 4]))) (setv res (list (take-while neg? [ -1 -4 5 3 4])))
(assert-false (= res [1 2]))) (assert-false (= res [1 2]))
(setv res (list (take-while none? [None None 1 2 3])))
(assert-equal res [None None])
(setv res (list (take-while (fn [x] (not (none? x))) [1 2 3 4 None 5 6 None 7])))
(assert-equal res [1 2 3 4]))