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]))