summaryrefslogtreecommitdiff
path: root/cljlib.fnl
diff options
context:
space:
mode:
Diffstat (limited to 'cljlib.fnl')
-rw-r--r--cljlib.fnl280
1 files changed, 261 insertions, 19 deletions
diff --git a/cljlib.fnl b/cljlib.fnl
index d429d09..d5c65c5 100644
--- a/cljlib.fnl
+++ b/cljlib.fnl
@@ -8,13 +8,35 @@
(require-macros :cljlib-macros)
(fn* core.vector
- "Constructs sequential table out of it's arguments."
+ "Constructs sequential table out of it's arguments.
+
+Sets additional metadata for function [`vector?`](#vector?) to work.
+
+# Examples
+
+``` fennel
+(local v (vector 1 2 3 4))
+(assert (eq v [1 2 3 4]))
+```"
[& args]
(setmetatable args {:cljlib/table-type :seq}))
(fn* core.apply
"Apply `f` to the argument list formed by prepending intervening
-arguments to `args`."
+arguments to `args`, adn `f` must support variadic amount of
+arguments.
+
+# Examples
+Applying `print` to different arguments:
+
+``` fennel
+(apply print [1 2 3 4])
+;; prints 1 2 3 4
+(apply print 1 [2 3 4])
+;; => 1 2 3 4
+(apply print 1 2 3 4 5 6 [7 8 9])
+;; => 1 2 3 4 5 6 7 8 9
+```"
([f args] (f (unpack args)))
([f a args] (f a (unpack args)))
([f a b args] (f a b (unpack args)))
@@ -34,24 +56,89 @@ arguments to `args`."
;; predicate functions
(fn* core.map?
- "Check whether `tbl` is an associative table."
+ "Check whether `tbl` is an associative table.
+
+Non empty associative tables are tested for two things:
+- `next` returns the key-value pair,
+- key, that is returned by the `next` is not equal to `1`.
+
+Empty tables can't be analyzed with this method, and `map?` will
+return `false`. If you need this test pass for empty table, see
+[`hash-map`](#hash-map) for creating tables that have additional
+metadata attached for this test to work.
+
+# Examples
+Non empty tables:
+
+``` fennel
+(assert (map? {:a 1 :b 2}))
+
+(local some-table {:key :value})
+(assert (map? some-table))
+```
+
+Empty tables:
+
+``` fennel
+(local some-table {})
+(assert (not (map? some-table)))
+```
+
+Empty tables created with [`hash-map`](#hash-map) will pass the test:
+
+``` fennel
+(local some-table (hash-map))
+(assert (map? some-table))
+```"
[tbl]
(if (= (type tbl) :table)
(if-let [t (fast-table-type tbl)]
(= t :table)
(let [(k _) (next tbl)]
(and (not= k nil)
- (or (not= (type k) :number)
- (not= k 1)))))))
+ (not= k 1))))))
(fn* core.vector?
- "Check whether `tbl` is an sequential table."
+ "Check whether `tbl` is an sequential table.
+
+Non empty sequential tables are tested for two things:
+- `next` returns the key-value pair,
+- key, that is returned by the `next` is equal to `1`.
+
+Empty tables can't be analyzed with this method, and `vector?` will
+always return `false`. If you need this test pass for empty table,
+see [`vector`](#vector) for creating tables that have additional
+metadata attached for this test to work.
+
+# Examples
+Non empty vector:
+
+``` fennel
+(assert (vector? [1 2 3 4]))
+
+(local some-table [1 2 3])
+(assert (vector? some-table))
+```
+
+Empty tables:
+
+``` fennel
+(local some-table [])
+(assert (not (vector? some-table)))
+```
+
+Empty tables created with [`vector`](#vector) will pass the test:
+
+``` fennel
+(local some-table (hash-map))
+(assert (vector? some-table))
+```"
[tbl]
(if (= (type tbl) :table)
(if-let [t (fast-table-type tbl)]
(= t :seq)
(let [(k _) (next tbl)]
- (and (not= k nil) (= (type k) :number) (= k 1))))))
+ (and (not= k nil) (= k 1))))))
(fn* core.nil?
@@ -60,7 +147,7 @@ arguments to `args`."
([x] (= x nil)))
(fn* core.zero?
- "Test if value is zero."
+ "Test if value is equal to zero."
[x]
(= x 0))
@@ -105,7 +192,9 @@ arguments to `args`."
(= x false))
(fn* core.int?
- "Test if `x` is a number without floating point data."
+ "Test if `x` is a number without floating point data.
+
+Number is rounded with `math.floor` and compared with original number."
[x]
(and (= (type x) :number)
(= x (math.floor x))))
@@ -146,10 +235,36 @@ arguments to `args`."
(fn* core.seq
"Create sequential table.
+
Transforms original table to sequential table of key value pairs
stored as sequential tables in linear time. If `col` is an
-associative table, returns `[[key1 value1] ... [keyN valueN]]` table.
-If `col` is sequential table, returns its shallow copy."
+associative table, returns sequential table of vectors with key and
+value. If `col` is sequential table, returns its shallow copy.
+
+# Examples
+Sequential tables remain as is:
+
+``` fennel
+(seq [1 2 3 4])
+;; [1 2 3 4]
+```
+
+Associative tables are transformed to format like this `[[key1 value1]
+... [keyN valueN]]` and order is non deterministic:
+
+``` fennel
+(seq {:a 1 :b 2 :c 3})
+;; [[:b 2] [:a 1] [:c 3]]
+```
+
+See `into` macros for transforming this back to associative table.
+Additionally you can use [`conj`](#conj) and [`apply`](#apply) with
+[`hash-map`](#hash-map):
+
+``` fennel
+(apply conj (hash-map) [:c 3] [[:a 1] [:b 2]])
+;; => {:a 1 :b 2 :c 3}
+```"
[col]
(let [res (empty [])]
(match (type col)
@@ -205,7 +320,43 @@ If `col` is sequential table, returns its shallow copy."
col)))
(fn* core.conj
- "Insert `x` as a last element of indexed table `tbl`. Modifies `tbl`"
+ "Insert `x` as a last element of a table `tbl`.
+
+If `tbl` is a sequential table or empty table, inserts `x` and
+optional `xs` as final element in the table.
+
+If `tbl` is an associative table, that satisfies [`map?`](#map?) test,
+insert `[key value]` pair into the table.
+
+Mutates `tbl`.
+
+# Examples
+Adding to sequential tables:
+
+``` fennel
+(conj [] 1 2 3 4)
+;; => [1 2 3 4]
+(conj [1 2 3] 4 5)
+;; => [1 2 3 4 5]
+```
+
+Adding to associative tables:
+
+``` fennel
+(conj {:a 1} [:b 2] [:c 3])
+;; => {:a 1 :b 2 :c 3}
+```
+
+Note, that passing literal empty associative table `{}` will not work:
+
+``` fennel
+(conj {} [:a 1] [:b 2])
+;; => [[:a 1] [:b 2]]
+(conj (hash-map) [:a 1] [:b 2])
+;; => {:a 1 :b 2}
+```
+
+See [`hash-map`](#hash-map) for creating empty associative tables."
([] (empty []))
([tbl] tbl)
([tbl x]
@@ -255,7 +406,22 @@ result of calling f with no arguments. If coll has only 1 item, it is
returned and f is not called. If val is supplied, returns the result
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. Calls `seq` on `col`."
+val and f is not called. Calls `seq` on `col`.
+
+Early termination is possible with the use of [`reduced`](#reduced)
+function.
+
+# Examples
+Reduce sequence of numbers with [`add`](#add)
+
+``` fennel
+(reduce add [1 2 3 4])
+;; => 10
+(reduce add 10 [1 2 3 4])
+;; => 20
+```
+
+"
([f col]
(let [col (or (seq col) (empty []))]
(match (length col)
@@ -278,7 +444,29 @@ val and f is not called. Calls `seq` on `col`."
(fn* core.reduced
"Wraps `x` in such a way so [`reduce`](#reduce) will terminate early
- with this value."
+with this value.
+
+# Examples
+Stop reduction is result is higher than `10`:
+
+``` fennel
+(reduce (fn [res x]
+ (if (>= res 10)
+ (reduced res)
+ (+ res x)))
+ [1 2 3])
+;; => 6
+
+(reduce (fn [res x]
+ (if (>= res 10)
+ (reduced res)
+ (+ res x)))
+ [1 2 3 4 :nil])
+;; => 10
+```
+
+Note that in second example we had `:nil` in the array, which is not a
+valid number, but we've terminated right before we've reached it."
[x]
(setmetatable
{} {:cljlib/reduced {:status :ready
@@ -292,7 +480,36 @@ applying `f` to `val`, the first key and the first value in `tbl`,
then applying `f` to that result and the 2nd key and value, etc. If
`tbl` contains no entries, returns `val` and `f` is not called. Note
that reduce-kv is supported on sequential tables and strings, where
-the keys will be the ordinals."
+the keys will be the ordinals.
+
+Early termination is possible with the use of [`reduced`](#reduced)
+function.
+
+# Examples
+Reduce associative table by adding values from all keys:
+
+``` fennel
+(local t {:a1 1
+ :b1 2
+ :a2 2
+ :b2 3})
+
+(reduce-kv #(+ $1 $3) 0 t)
+;; => 8
+```
+
+Reduce table by adding values from keys that start with letter `a`:
+
+``` fennel
+(local t {:a1 1
+ :b1 2
+ :a2 2
+ :b2 3})
+
+(reduce-kv (fn [res k v] (if (= (string.sub k 1 1) :a) (+ res v) res))
+ 0 t)
+;; => 3
+```"
[f val tbl]
(var res val)
(each [_ [k v] (pairs (or (seq tbl) (empty [])))]
@@ -308,12 +525,37 @@ the keys will be the ordinals."
(fn* core.mapv
"Maps function `f` over one or more collections.
-Accepts arbitrary amount of tables, calls `seq` on each of it.
-Function `f` must take the same amount of parameters as the amount of
-tables passed to `mapv`. Applies `f` over first value of each
+Accepts arbitrary amount of collections, calls `seq` on each of it.
+Function `f` must take the same amount of arguments as the amount of
+tables, passed to `mapv`. Applies `f` over first value of each
table. Then applies `f` to second value of each table. Continues until
any of the tables is exhausted. All remaining values are
-ignored. Returns a table of results."
+ignored. Returns a sequential table of results.
+
+# Examples
+Map `string.upcase` over the string:
+
+``` fennel
+(mapv string.upper \"string\")
+;; => [\"S\" \"T\" \"R\" \"I\" \"N\" \"G\"]
+```
+
+Map [`mul`](#mul) over two tables:
+
+``` fennel
+(mapv mul [1 2 3 4] [1 0 -1])
+;; => [1 0 -3]
+```
+
+Basic `zipmap` implementation:
+
+``` fennel
+(fn zipmap [keys vals]
+ (into {} (mapv vector keys vals)))
+
+(zipmap [:a :b :c] [1 2 3 4])
+;; => {:a 1 :b 2 :c 3}
+```"
([f col]
(local res (empty []))
(each [_ v (ipairs (or (seq col) (empty [])))]