From 41ae4f4d2c2091e9d78bb9acd41a40c92e462cf9 Mon Sep 17 00:00:00 2001 From: Bob Tolbert Date: Sun, 7 Jul 2013 20:36:26 -0600 Subject: [PATCH] Update: 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. --- hy/core/language.hy | 94 +++++++++++++++++++++---------------- tests/native_tests/core.hy | 96 +++++++++++++++++++++++++++++++------- 2 files changed, 133 insertions(+), 57 deletions(-) diff --git a/hy/core/language.hy b/hy/core/language.hy index 6cba788..db6a265 100644 --- a/hy/core/language.hy +++ b/hy/core/language.hy @@ -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] "Yield an infinite repetition of the items in coll" - (while true - (for [x coll] + (setv seen []) + (for [x coll] + (yield x) + (.append seen x)) + (while seen + (for [x seen] (yield x)))) (defn dec [n] "Decrement n by 1" + (_numeric-check n) (- n 1)) (defn distinct [coll] "Return a generator from the original collection with duplicates removed" - (let [ [seen [] ] - [citer (iter coll)] - [val (next citer)] ] - (while (not (none? val)) - (do - (if (not_in val seen) - (do - (yield val) - (.append seen val))) - (setv val (next citer)))))) + (let [[seen []] [citer (iter coll)]] + (for [val citer] + (if (not_in val seen) + (do + (yield val) + (.append seen val)))))) (defn drop [count coll] "Drop `count` elements from `coll` and yield back the rest" - (let [ [citer (iter coll)] ] + (let [[citer (iter coll)]] (try (for [i (range count)] (next citer)) (catch [StopIteration])) @@ -37,18 +42,19 @@ (defn even? [n] "Return true if n is an even number" + (_numeric-check n) (= (% n 2) 0)) (defn filter [pred coll] - "Return all elements from coll that pass filter" - (let [ [citer (iter coll)] [val (next citer)] ] - (while (not (none? val)) + "Return all elements from `coll` that pass `pred`" + (let [[citer (iter coll)]] + (for [val citer] (if (pred val) - (yield val)) - (setv val (next citer))))) + (yield val))))) (defn inc [n] "Increment n by 1" + (_numeric-check n) (+ n 1)) (defn instance? [klass x] @@ -72,12 +78,17 @@ (defn neg? [n] "Return true if n is < 0" + (_numeric-check n) (< n 0)) (defn none? [x] "Return true if x is None" (is x None)) +(defn numeric? [x] + (import numbers) + (instance? numbers.Number x)) + (defn nth [coll index] "Return nth item in collection or sequence, counting from 0" (if (not (neg? index)) @@ -90,19 +101,20 @@ (defn odd? [n] "Return true if n is an odd number" + (_numeric-check n) (= (% n 2) 1)) (defn pos? [n] "Return true if n is > 0" + (_numeric_check n) (> n 0)) (defn remove [pred coll] "Return coll with elements removed that pass `pred`" - (let [ [citer (iter coll)] [val (next citer)] ] - (while (not (none? val)) + (let [[citer (iter coll)]] + (for [val citer] (if (not (pred val)) - (yield val)) - (setv val (next citer))))) + (yield val))))) (defn repeat [x &optional n] "Yield x forever or optionally n times" @@ -116,33 +128,33 @@ (while true (yield (func)))) -(defn take [count what] - "Take `count` elements from `what`, or the whole set if the total - number of entries in `what` is less than `count`." - (setv what (iter what)) - (for [i (range count)] - (yield (next what)))) +(defn take [count coll] + "Take `count` elements from `coll`, or the whole set if the total + number of entries in `coll` is less than `count`." + (let [[citer (iter coll)]] + (for [_ (range count)] + (yield (next citer))))) (defn take-nth [n coll] "Return every nth member of coll raises ValueError for (not (pos? n))" (if (pos? n) - (let [ [citer (iter coll)] [val (next citer)] ] - (while (not (none? val)) + (let [[citer (iter coll)] [skip (dec n)]] + (for [val citer] (yield val) - (for [_ (range (dec n))] - (next citer)) - (setv val (next citer)))) + (for [_ (range skip)] + (next citer)))) (raise (ValueError "n must be positive")))) (defn take-while [pred coll] "Take all elements while `pred` is true" - (let [ [citer (iter coll)] [val (next citer)] ] - (while (pred val) - (yield val) - (setv val (next citer))))) + (let [[citer (iter coll)]] + (for [val citer] + (if (pred val) + (yield val) + (break))))) -(def *exports* ["cycle" "dec" "distinct" "drop" "even?" "filter" "inc" - "instance?" "iterable?" "iterate" "iterator?" "neg?" - "none?" "nth" "odd?" "pos?" "remove" "repeat" "repeatedly" - "take" "take_nth" "take_while"]) +(def *exports* ["cycle" "dec" "distinct" "drop" "even?" "filter" "inc" + "instance?" "iterable?" "iterate" "iterator?" "neg?" + "none?" "nth" "numeric?" "odd?" "pos?" "remove" "repeat" + "repeatedly" "take" "take_nth" "take_while"]) diff --git a/tests/native_tests/core.hy b/tests/native_tests/core.hy index 9ab80cf..ff9464a 100644 --- a/tests/native_tests/core.hy +++ b/tests/native_tests/core.hy @@ -13,14 +13,22 @@ (defn test-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 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 [] "NATIVE: testing the dec function" (assert-equal 0 (dec 1)) (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 [] "NATIVE: testing the distinct function" @@ -32,14 +40,19 @@ ;; now with an iter (setv test_iter (iter [1 2 3 4 3 5 2])) (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 [] "NATIVE: testing drop function" (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])))) - (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]))) (assert-equal res [1 2 3 4 5]) (setv res (list (drop -1 [1 2 3 4 5]))) @@ -53,7 +66,13 @@ "NATIVE: testing the even? function" (assert-true (even? -2)) (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 [] "NATIVE: testing the filter function" @@ -63,12 +82,26 @@ (setv res (list (filter pos? (iter [ 1 2 3 -4 5 -6])))) (assert-equal res [ 1 2 3 5]) (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 [] "NATIVE: testing the inc function" (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 [] "NATIVE: testing instance? function" @@ -140,7 +173,13 @@ "NATIVE: testing the neg? function" (assert-true (neg? -2)) (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 [] "NATIVE: testing for `is None`" @@ -166,13 +205,25 @@ "NATIVE: testing the odd? function" (assert-true (odd? -3)) (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 [] "NATIVE: testing the pos? function" (assert-true (pos? 2)) (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 [] "NATIVE: testing the remove function" @@ -180,7 +231,9 @@ (assert-equal r [2 4 6]) (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 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 [] "NATIVE: testing repeat" @@ -206,8 +259,10 @@ (setv res (list (take 0 (repeat "s")))) (assert-equal res []) (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 [] "NATIVE: testing the take-nth function" (setv res (list (take-nth 2 [1 2 3 4 5 6 7]))) @@ -222,6 +277,11 @@ (assert-equal res [1 7]) (setv res (list (take-nth 7 [1 2 3 4 5 6 7]))) (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 (let [[passed false]] (try @@ -232,6 +292,10 @@ (defn test-take-while [] "NATIVE: testing the take-while function" (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]))) - (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]))