summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.org11
-rw-r--r--core.fnl89
-rw-r--r--core_test.fnl35
3 files changed, 102 insertions, 33 deletions
diff --git a/README.org b/README.org
index 7949fb2..4ddaf56 100644
--- a/README.org
+++ b/README.org
@@ -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.
diff --git a/core.fnl b/core.fnl
index c22cc49..778f2c6 100644
--- a/core.fnl
+++ b/core.fnl
@@ -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]))