diff options
| -rw-r--r-- | README.org | 11 | ||||
| -rw-r--r-- | core.fnl | 89 | ||||
| -rw-r--r-- | core_test.fnl | 35 |
3 files changed, 102 insertions, 33 deletions
@@ -100,6 +100,7 @@ Much like =if-let= and =when-let=, except tests expression for =nil=. #+end_src *** =into= +<<into>> Clojure's =into= function implemented as macro, because Fennel has no runtime distinction between =[]= and ={}= tables, since Lua also doesn't feature this feature. However we can do this at compile time. @@ -152,7 +153,7 @@ Full set can be examined by requiring the module. *** =seq= =seq= produces a sequential table from any kind of table in linear time. -Works mostly like in Clojure, but, since Fennel doesn't have list object, it always returns sequential tables: +Works mostly like in Clojure, but, since Fennel doesn't have list object, it returns sequential table or =nil=: #+begin_src fennel (seq [1 2 3 4 5]) @@ -160,9 +161,15 @@ Works mostly like in Clojure, but, since Fennel doesn't have list object, it alw (seq {:a 1 :b 2 :c 3 :d 4}) ;; [["d" 4] ["a" 1] ["b" 2] ["c" 3]] + + (seq []) + ;; nil + + (seq {}) + ;; nil #+end_src -See [[*=into=][=into=]] on how to transform such sequence back into associative table. +See [[into][into]] on how to transform such sequence back into associative table. *** =first= and =rest= =first= returns first value of a table. @@ -1,7 +1,7 @@ (local insert table.insert) (local _unpack (or table.unpack unpack)) (import-macros {: fn*} :macros.fn) -(import-macros {: when-some} :macros.core) +(import-macros {: when-some : if-some : when-let} :macros.core) (fn seq [tbl] "Create sequential table. @@ -9,23 +9,30 @@ Transforms original table to sequential table of key value pairs stored as sequential tables in linear time. If `tbl' is an associative table, returns `[[key1 value1] ... [keyN valueN]]' table. If `tbl' is sequential table, leaves it unchanged." - (var assoc? false) - (let [res []] - (each [k v (pairs tbl)] - (if (and (not assoc?) - (not (= (type k) "number"))) - (set assoc? true)) - (insert res [k v])) - (if assoc? res tbl))) + (when-some [_ (and tbl (next tbl))] + (var assoc? false) + (let [res []] + (each [k v (pairs tbl)] + (if (and (not assoc?) + (not (= (type k) "number"))) + (set assoc? true)) + (insert res [k v])) + (if assoc? res tbl)))) + +(macro safe-seq [tbl] + `(or (seq ,tbl) [])) (fn first [tbl] "Return first element of an indexed table." - (. (seq tbl) 1)) + (when-some [tbl tbl] + (. (seq tbl) 1))) (fn rest [tbl] "Returns table of all elements of indexed table but the first one." - [(_unpack (seq tbl) 2)]) + (if-some [tbl tbl] + [(_unpack (seq tbl) 2)] + [])) (fn* conj @@ -75,17 +82,20 @@ of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc. If coll contains no items, returns val and f is not called." ([f tbl] - (let [tbl (seq tbl)] + (when-some [tbl (seq tbl)] (match (length tbl) 0 (f) 1 (. tbl 1) 2 (f (. tbl 1) (. tbl 2)) _ (let [[a b & rest] tbl] (reduce f (f a b) rest))))) - ([f val [x & xs]] - (if (not (= x nil)) - (reduce f (f val x) xs) - val))) + ([f val tbl] + (if-some [tbl (seq tbl)] + (let [[x & xs] tbl] + (if (not (= x nil)) + (reduce f (f val x) xs) + val)) + val))) (fn* reduce-kv "Reduces an associative table using function `f' and initial value `val'. @@ -99,7 +109,7 @@ contains no entries, returns `val' and `f' is not called. Note that reduce-kv is supported on vectors, where the keys will be the ordinals." [f val tbl] (var res val) - (each [_ [k v] (pairs (seq tbl))] + (each [_ [k v] (pairs (safe-seq tbl))] (set res (f res k v))) res) @@ -114,14 +124,14 @@ any of the tables is exhausted. All remaining values are ignored. Returns a table of results." ([f tbl] (local res []) - (each [_ v (ipairs (seq tbl))] + (each [_ v (ipairs (safe-seq tbl))] (when-some [tmp (f v)] (insert res tmp))) res) ([f t1 t2] (let [res [] - t1 (seq t1) - t2 (seq t2)] + t1 (safe-seq t1) + t2 (safe-seq t2)] (var (i1 v1) (next t1)) (var (i2 v2) (next t2)) (while (and i1 i2) @@ -132,9 +142,9 @@ ignored. Returns a table of results." res)) ([f t1 t2 t3] (let [res [] - t1 (seq t1) - t2 (seq t2) - t3 (seq t3)] + t1 (safe-seq t1) + t2 (safe-seq t2) + t3 (safe-seq t3)] (var (i1 v1) (next t1)) (var (i2 v2) (next t2)) (var (i3 v3) (next t3)) @@ -150,13 +160,20 @@ ignored. Returns a table of results." (when (->> tbls (mapv #(~= (next $) nil)) (reduce #(and $1 $2))) - (cons (mapv #(first (seq $)) tbls) (step (mapv rest tbls))))) + (cons (mapv #(first (safe-seq $)) tbls) (step (mapv rest tbls))))) res []] (each [_ v (ipairs (step (consj tbls t3 t2 t1)))] (when-some [tmp (f (_unpack v))] (insert res tmp))) res))) +(fn filter [pred tbl] + (when-let [tbl (seq tbl)] + (let [f (first tbl) r (rest tbl)] + (if (pred f) + (cons f (filter pred r)) + (filter pred r))))) + (fn kvseq [tbl] "Transforms any table kind to key-value sequence." (let [res []] @@ -196,10 +213,12 @@ ignored. Returns a table of results." (pred (first tbl)) (every? pred (rest tbl)) false)) -;; (fn* some -;; [pred itbl] -;; (if (> (length itbl) 0) -;; )) +(fn* some + [pred tbl] + (when-let [tbl (seq tbl)] + (or (pred (first tbl)) (some pred (rest tbl))))) + +(local not-any? (comp #(not $) some)) (fn* range "return range of of numbers from `lower' to `upper' with optional `step'." @@ -211,8 +230,16 @@ ignored. Returns a table of results." (insert res i)) res))) +(fn even? [x] + (when-some [x x] + (= (% x 2) 0))) + +(fn odd? [x] + (not (even? x))) + {: seq : mapv + : filter : reduce : reduce-kv : conj @@ -223,4 +250,8 @@ ignored. Returns a table of results." : identity : comp : every? - : range} + : some + : not-any? + : range + : even? + : odd?} diff --git a/core_test.fnl b/core_test.fnl index 673b007..fe2a71c 100644 --- a/core_test.fnl +++ b/core_test.fnl @@ -2,7 +2,25 @@ (import-macros {: into} :macros.core) (import-macros {: assert-eq : assert-ne : assert* : test} :test) -(local {: seq : mapv : mapkv : reduce : reduce-kv : conj : cons : consj : first : rest : eq? : identity : comp : every? : range} (require :core)) +(local {: seq + : mapv + : filter + : reduce + : reduce-kv + : conj + : cons + : first + : rest + : eq? + : identity + : comp + : every? + : some + : not-any? + : range + : even? + : odd?} + (require :core)) (test equality-test ;; comparing basetypes @@ -70,9 +88,18 @@ "Alice Watson works as chief officer at Coffee With You"])) (test reduce-test - (fn ++ [a b] (+ a b)) + (fn* ++ + ([] 0) + ([a] a) + ([a b] (+ a b)) + ([a b & c] + (var res (+ a b)) + (each [_ v (ipairs c)] + (set res (+ res v))) + res)) (assert-eq (reduce ++ (range 10)) 45) (assert-eq (reduce ++ -3 (range 10)) 42) + (assert-eq (reduce ++ 10 nil) 10) (fn mapping [f] @@ -86,3 +113,7 @@ init)) (assert-eq (reduce ++ (range 10)) (reduce- ++ 0 (range 10)))) + +(test filter-test + (assert-eq (filter even? (range 10)) [0 2 4 6 8]) + (assert-eq (filter odd? (range 10)) [1 3 5 7 9])) |