nydp 0.4.0 → 0.4.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +44 -0
- data/lib/lisp/core-010-precompile.nydp +13 -16
- data/lib/lisp/core-012-utils.nydp +21 -6
- data/lib/lisp/core-015-documentation.nydp +60 -19
- data/lib/lisp/core-017-builtin-dox.nydp +50 -39
- data/lib/lisp/core-020-utils.nydp +5 -5
- data/lib/lisp/core-030-syntax.nydp +103 -61
- data/lib/lisp/core-035-flow-control.nydp +18 -9
- data/lib/lisp/core-037-list-utils.nydp +36 -14
- data/lib/lisp/core-039-module.nydp +24 -0
- data/lib/lisp/core-040-utils.nydp +41 -23
- data/lib/lisp/core-041-string-utils.nydp +37 -9
- data/lib/lisp/core-042-date-utils.nydp +21 -1
- data/lib/lisp/core-043-list-utils.nydp +93 -67
- data/lib/lisp/core-045-dox-utils.nydp +5 -0
- data/lib/lisp/core-080-pretty-print.nydp +55 -17
- data/lib/lisp/core-090-hook.nydp +35 -1
- data/lib/lisp/core-100-utils.nydp +130 -28
- data/lib/lisp/core-110-hash-utils.nydp +61 -0
- data/lib/lisp/core-120-settings.nydp +46 -0
- data/lib/lisp/core-130-validations.nydp +51 -0
- data/lib/lisp/{core-060-benchmarking.nydp → core-900-benchmarking.nydp} +108 -5
- data/lib/lisp/tests/accum-examples.nydp +28 -1
- data/lib/lisp/tests/aif-examples.nydp +8 -3
- data/lib/lisp/tests/andify-examples.nydp +7 -0
- data/lib/lisp/tests/at-syntax-examples.nydp +17 -0
- data/lib/lisp/tests/best-examples.nydp +9 -0
- data/lib/lisp/tests/builtin-tests.nydp +19 -0
- data/lib/lisp/tests/case-examples.nydp +14 -0
- data/lib/lisp/tests/cdr-set-examples.nydp +6 -0
- data/lib/lisp/tests/date-examples.nydp +56 -1
- data/lib/lisp/tests/destructuring-examples.nydp +5 -5
- data/lib/lisp/tests/detect-examples.nydp +12 -0
- data/lib/lisp/tests/dp-examples.nydp +24 -0
- data/lib/lisp/tests/empty-examples.nydp +1 -1
- data/lib/lisp/tests/error-tests.nydp +4 -4
- data/lib/lisp/tests/filter-forms-examples.nydp +30 -0
- data/lib/lisp/tests/foundation-test.nydp +12 -0
- data/lib/lisp/tests/hash-examples.nydp +26 -2
- data/lib/lisp/tests/list-grep-examples.nydp +40 -0
- data/lib/lisp/tests/list-tests.nydp +58 -1
- data/lib/lisp/tests/map-hash-examples.nydp +11 -0
- data/lib/lisp/tests/mapreduce-examples.nydp +10 -0
- data/lib/lisp/tests/module-examples.nydp +10 -0
- data/lib/lisp/tests/multi-assign-examples.nydp +6 -0
- data/lib/lisp/tests/parser-tests.nydp +21 -0
- data/lib/lisp/tests/pretty-print-tests.nydp +16 -13
- data/lib/lisp/tests/set-difference-examples.nydp +8 -0
- data/lib/lisp/tests/set-intersection-examples.nydp +32 -0
- data/lib/lisp/tests/set-union-examples.nydp +24 -0
- data/lib/lisp/tests/settings-examples.nydp +40 -0
- data/lib/lisp/tests/sort-examples.nydp +8 -0
- data/lib/lisp/tests/string-tests.nydp +61 -1
- data/lib/lisp/tests/syntax-tests.nydp +5 -1
- data/lib/lisp/tests/to-integer-examples.nydp +16 -0
- data/lib/lisp/tests/validation-examples.nydp +15 -0
- data/lib/lisp/tests/zap-examples.nydp +12 -0
- data/lib/nydp.rb +13 -7
- data/lib/nydp/assignment.rb +10 -3
- data/lib/nydp/builtin.rb +1 -1
- data/lib/nydp/builtin/abs.rb +8 -0
- data/lib/nydp/builtin/cdr_set.rb +1 -6
- data/lib/nydp/builtin/date.rb +15 -1
- data/lib/nydp/builtin/error.rb +1 -1
- data/lib/nydp/builtin/handle_error.rb +1 -1
- data/lib/nydp/builtin/hash.rb +27 -45
- data/lib/nydp/builtin/inspect.rb +1 -1
- data/lib/nydp/builtin/plus.rb +10 -2
- data/lib/nydp/builtin/rand.rb +18 -0
- data/lib/nydp/builtin/random_string.rb +2 -2
- data/lib/nydp/builtin/ruby_wrap.rb +72 -0
- data/lib/nydp/builtin/set_intersection.rb +8 -0
- data/lib/nydp/builtin/set_union.rb +8 -0
- data/lib/nydp/builtin/string_match.rb +2 -2
- data/lib/nydp/builtin/string_pad_left.rb +7 -0
- data/lib/nydp/builtin/string_pad_right.rb +7 -0
- data/lib/nydp/builtin/string_replace.rb +1 -1
- data/lib/nydp/builtin/string_split.rb +1 -2
- data/lib/nydp/builtin/to_integer.rb +23 -0
- data/lib/nydp/builtin/to_string.rb +2 -9
- data/lib/nydp/builtin/type_of.rb +9 -6
- data/lib/nydp/closure.rb +0 -3
- data/lib/nydp/cond.rb +23 -1
- data/lib/nydp/context_symbol.rb +14 -6
- data/lib/nydp/core.rb +45 -33
- data/lib/nydp/core_ext.rb +54 -0
- data/lib/nydp/date.rb +37 -31
- data/lib/nydp/function_invocation.rb +34 -26
- data/lib/nydp/hash.rb +5 -6
- data/lib/nydp/helper.rb +41 -25
- data/lib/nydp/interpreted_function.rb +68 -40
- data/lib/nydp/literal.rb +1 -1
- data/lib/nydp/pair.rb +25 -9
- data/lib/nydp/parser.rb +8 -6
- data/lib/nydp/string_atom.rb +16 -22
- data/lib/nydp/symbol.rb +40 -27
- data/lib/nydp/symbol_lookup.rb +7 -7
- data/lib/nydp/tokeniser.rb +2 -2
- data/lib/nydp/truth.rb +17 -10
- data/lib/nydp/version.rb +1 -1
- data/lib/nydp/vm.rb +7 -2
- data/nydp.gemspec +2 -4
- data/spec/date_spec.rb +115 -22
- data/spec/embedded_spec.rb +12 -12
- data/spec/foreign_hash_spec.rb +14 -2
- data/spec/hash_non_hash_behaviour_spec.rb +7 -7
- data/spec/hash_spec.rb +24 -2
- data/spec/nydp_spec.rb +14 -2
- data/spec/parser_spec.rb +27 -16
- data/spec/rand_spec.rb +45 -0
- data/spec/spec_helper.rb +13 -1
- data/spec/symbol_spec.rb +31 -0
- data/spec/time_spec.rb +1 -1
- metadata +38 -37
- data/lib/nydp/builtin/car.rb +0 -7
- data/lib/nydp/builtin/cdr.rb +0 -7
- data/lib/nydp/builtin/cons.rb +0 -9
@@ -40,21 +40,30 @@
|
|
40
40
|
(loop (assign ,v ,gi) (< ,v ,gm) (assign ,v (+ ,v 1))
|
41
41
|
,@body))))
|
42
42
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
43
|
+
;; return a new function which is the original function with
|
44
|
+
;; the given args1 already applied
|
45
|
+
;; arguments to the new function are whatever arguments remain
|
46
|
+
;; for the old function
|
47
|
+
;; Could be (mac curry things `(fn args (apply ,@things args))) but less readable
|
48
|
+
(mac curry (f . args0)
|
49
|
+
`(fn args
|
50
|
+
(apply ,f ,@args0 args)))
|
51
|
+
|
52
|
+
;; like curry, but the returned function takes only a single arg (assumes all
|
53
|
+
;; args but one are provided here)
|
54
|
+
;; Could be (mac curry1 things `(fn (arg) (,@things arg))) but less readable
|
55
|
+
(mac curry1 (f . args)
|
56
|
+
`(fn (arg)
|
57
|
+
(,f ,@args arg)))
|
49
58
|
|
50
59
|
(mac cache-get (hsh key val)
|
51
60
|
; if ,key is already in ,hsh - return the associated value.
|
52
61
|
; if ,key is not already in ,hsh - evaluate ,val, store the result
|
53
62
|
; under ,key in ,hsh, and return it
|
54
63
|
(w/uniq (h k v)
|
55
|
-
|
56
|
-
|
57
|
-
|
64
|
+
`(with (,h ,hsh ,k ,key)
|
65
|
+
(let ,v (hash-get ,h ,k)
|
66
|
+
(or ,v (returnlet ,v ,val (hash-set ,h ,k ,v)))))))
|
58
67
|
|
59
68
|
(mac defmemo (name args . body)
|
60
69
|
; same as 'def, but caches the result, keyed on args, so for a given set of args the result
|
@@ -6,35 +6,57 @@
|
|
6
6
|
(cons (map car args)
|
7
7
|
(apply zip (map cdr args)))))
|
8
8
|
|
9
|
+
; invokes 'f for each element of 'things, first element processed first
|
10
|
+
; ( "l" in "eachl" = "leftmost first" )
|
11
|
+
(def eachl (f things)
|
12
|
+
(when things
|
13
|
+
(f (car things))
|
14
|
+
(eachl f (cdr things))))
|
15
|
+
|
16
|
+
;; if things is a pair,
|
17
|
+
;; if (cdr things) is nil, return (car things)
|
18
|
+
;; else recurse on (cdr things)
|
19
|
+
;; else return things
|
20
|
+
(def list/last (things)
|
21
|
+
(if (pair? things)
|
22
|
+
(aif (cdr things)
|
23
|
+
(list/last it)
|
24
|
+
(car things))
|
25
|
+
things))
|
26
|
+
|
27
|
+
; invokes 'f for each element of 'things, last element processed first
|
28
|
+
; ( "r" in "eachr" = "rightmost first" )
|
9
29
|
(def eachr (f things)
|
10
30
|
(when things
|
11
31
|
(eachr f (cdr things))
|
12
32
|
(f (car things))))
|
13
33
|
|
14
|
-
|
15
|
-
|
16
|
-
`(= ,things (cons ,x ,things)))
|
34
|
+
; assign (cons x things) to things
|
35
|
+
(mac push (x things) `(= ,things (cons ,x ,things)))
|
17
36
|
|
18
|
-
|
19
|
-
|
37
|
+
; flatten the given list, transforming each leaf-item, recursively
|
38
|
+
(def flatmap (f things)
|
20
39
|
(let acc nil
|
21
40
|
(rfnwith flattenize (x things)
|
22
41
|
(if (pair? x)
|
23
42
|
(eachr flattenize x)
|
24
|
-
|
43
|
+
x
|
44
|
+
(push (f x) acc)))
|
25
45
|
acc))
|
26
46
|
|
47
|
+
; flatten the given list, recursively
|
48
|
+
(def flatten (things) (flatmap x1 things))
|
49
|
+
|
50
|
+
; given a list 'al of form '( (k0 v0) (k1 v1) (k2 v2) ... (kn vn) ) and
|
51
|
+
; a 'key, returns the list (kx vx) from 'al where kx is equal to 'key
|
52
|
+
; #attribution: inspiration from arc.arc
|
27
53
|
(def assoc (key al)
|
28
|
-
; given a list 'al of form '( (k0 v0) (k1 v1) (k2 v2) ... (kn vn) ) and
|
29
|
-
; a 'key, returns the list (kx vx) from 'al where kx is equal to 'key
|
30
|
-
; #attribution: inspiration from arc.arc
|
31
54
|
(if (pair? al)
|
32
55
|
(if (caris key (car al))
|
33
56
|
(car al)
|
34
57
|
(assoc key (cdr al)))))
|
35
58
|
|
36
|
-
(
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
(cadr (assoc key al)))
|
59
|
+
; given a list 'al of form '( (k0 v0) (k1 v1) (k2 v2) ... (kn vn) ) and
|
60
|
+
; a 'key, returns vx from 'al where kx is equal to 'key
|
61
|
+
; #attribution: lifted almost directly from arc.arc
|
62
|
+
(def alref (key al) (cadr (assoc key al)))
|
@@ -0,0 +1,24 @@
|
|
1
|
+
;; creates a private namespace for support functions for one or more explicitly exported/public functions
|
2
|
+
;;
|
3
|
+
;; (module foo
|
4
|
+
;; (def h ...)
|
5
|
+
;; (def u ...)
|
6
|
+
;; (export bar (x y) (h u x y)))
|
7
|
+
;;
|
8
|
+
;; results in 'foo/bar being universally available, but 'h and 'u are visible only within the module and override
|
9
|
+
;; any other 'h or 'u defined elsewhere, in the scope of the module.
|
10
|
+
(mac module (module-name . forms)
|
11
|
+
(let private-names nil
|
12
|
+
(let module-forms
|
13
|
+
{ def (fn (name args . body)
|
14
|
+
(push nil private-names)
|
15
|
+
(push name private-names)
|
16
|
+
`(assign ,name (fn ,args ,@body)))
|
17
|
+
export-def macs.def
|
18
|
+
export (fn (name args . body)
|
19
|
+
`(export-def
|
20
|
+
,(sym (+ (to-string module-name) "/" (to-string name)))
|
21
|
+
,args
|
22
|
+
,@body)) }
|
23
|
+
(let module-body (pre-compile-each module-forms forms)
|
24
|
+
`(with (,@private-names) ,@module-body)))))
|
@@ -11,22 +11,20 @@
|
|
11
11
|
(def num? (arg) (comment "true if arg is a number") (isa 'number arg))
|
12
12
|
(def string? (arg) (comment "true if arg is a string") (isa 'string arg))
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
expr)
|
14
|
+
;; this is useful sometimes when 'expr can't stand on its own due to lexical ambiguity, most often in string interpolations
|
15
|
+
;; for example, in "hello ~person, how are you", the parser will try to interpolate the symbol "person," rather than the
|
16
|
+
;; expected "person". In this case, use "hello ~(just person), how are you"
|
17
|
+
(mac just (expr) expr)
|
19
18
|
|
20
19
|
(def quotify (arg) `(quote ,arg))
|
21
20
|
|
22
|
-
|
23
|
-
|
24
|
-
(fn nil arg))
|
21
|
+
;; return a function that always returns 'arg, similar to K in SKI calculus
|
22
|
+
(defmemo k (arg) (fn nil arg))
|
25
23
|
|
24
|
+
;; return the length of 'things where 'things may be nil, a string, list or hash
|
25
|
+
;; length of nil is zero, length of hash is number of keys, length of string
|
26
|
+
;; is number of characters, length of list is number of direct items - no recursive counting
|
26
27
|
(def len (things)
|
27
|
-
; return the length of 'things where 'things may be nil, a string, list or hash
|
28
|
-
; length of nil is zero, length of hash is number of keys, length of string
|
29
|
-
; is number of characters, length of list is number of direct items - no recursive counting
|
30
28
|
(chapter list-manipulation)
|
31
29
|
(chapter string-manipulation)
|
32
30
|
(chapter hash-manipulation)
|
@@ -38,25 +36,34 @@
|
|
38
36
|
|
39
37
|
(assign dynamics (hash))
|
40
38
|
|
41
|
-
|
42
|
-
|
43
|
-
(
|
44
|
-
(let with-mac-name (sym "w/~name")
|
39
|
+
;; creates a dynamic variable.
|
40
|
+
(mac dynamic (name initial)
|
41
|
+
(let with-mac-name (sym:+ "w/" name)
|
45
42
|
(w/uniq prev
|
46
43
|
`(do
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
44
|
+
(hash-set dynamics ',name t)
|
45
|
+
(mac ,with-mac-name (new-value . body)
|
46
|
+
(w/uniq result
|
47
|
+
`(let ,',prev (hash-get (thread-locals) ',',name)
|
48
|
+
(hash-set (thread-locals) ',',name ,new-value)
|
49
|
+
(returning (do ,@body)
|
50
|
+
(hash-set (thread-locals) ',',name ,',prev)))))
|
51
|
+
,(if initial `(hash-set (thread-locals) ',name ,initial))
|
52
|
+
(def ,name () (hash-get (thread-locals) ',name))))))
|
53
|
+
|
54
|
+
|
55
|
+
;; overrides 'privately defined earlier in documentation manager
|
56
|
+
(dynamic privately)
|
57
|
+
|
58
|
+
;; suppress documentation of anything defined in 'body
|
59
|
+
(mac in-private body
|
60
|
+
`(w/privately t ,@body))
|
55
61
|
|
56
62
|
(mac mapx (things x expr)
|
57
63
|
; a macro wrapper for 'map
|
58
64
|
; 'things is a list, 'x is the name of a variable, and 'expr
|
59
65
|
; is evaluated and collected for each 'x in 'things
|
66
|
+
; usage: (mapx items v (to-string v)) equivalent to (map to-string items)
|
60
67
|
(chapter list-manipulation)
|
61
68
|
`(map (fun (,x) ,expr) ,things))
|
62
69
|
|
@@ -83,3 +90,14 @@
|
|
83
90
|
(chapter string-manipulation)
|
84
91
|
(chapter hash-manipulation)
|
85
92
|
(!empty? thing))
|
93
|
+
|
94
|
+
;; returns the first non-empty item in 'args
|
95
|
+
;; mac equivalent of (detect present? args)
|
96
|
+
(mac dp args
|
97
|
+
(if args
|
98
|
+
(w/uniq nearg
|
99
|
+
`(let ,nearg ,(car args)
|
100
|
+
(if (empty? ,nearg)
|
101
|
+
(dp ,@(cdr args))
|
102
|
+
,nearg)))
|
103
|
+
nil))
|
@@ -1,7 +1,8 @@
|
|
1
1
|
(chapter-start 'string-manipulation "utilities for manipulating strings")
|
2
2
|
|
3
|
+
; return a new string with leading and trailing whitespace removed
|
3
4
|
(def string-strip (txt)
|
4
|
-
(string-replace "(
|
5
|
+
(string-replace "(\\A\\s+|\\s+\\z)" "" txt))
|
5
6
|
|
6
7
|
(def joinstr (txt . things)
|
7
8
|
; flatten 'things into a single list (ie unnest lists)
|
@@ -15,14 +16,41 @@
|
|
15
16
|
(flatten (map (fn (x) (list txt x))
|
16
17
|
(cdr joinables))))))
|
17
18
|
|
18
|
-
(
|
19
|
-
|
20
|
-
|
21
|
-
(joinstr "" items))
|
19
|
+
; stringify join all the things and join them with no separator, like (joinstr "" things)
|
20
|
+
(def j things
|
21
|
+
(apply + (flatmap to-string things)))
|
22
22
|
|
23
|
+
; string-interpolation syntax emits this form. Default implementation
|
24
|
+
; is to delegate to 'j , but containing forms may use macros that
|
25
|
+
; override this in order to provide specific interpolation behaviour
|
26
|
+
; (for example, formatting numbers or stripping HTML tags)
|
23
27
|
(def string-pieces pieces
|
24
|
-
; string-interpolation syntax emits this form. Default implementation
|
25
|
-
; is to delegate to 'j , but containing forms may use macros that
|
26
|
-
; override this in order to provide specific interpolation behaviour
|
27
|
-
; (for example, formatting numbers or stripping HTML tags)
|
28
28
|
(j pieces))
|
29
|
+
|
30
|
+
; return the first 'length chars of string 'str
|
31
|
+
(def string-truncate (str length)
|
32
|
+
(string-replace "(.{~|length|}).*" "\\1" str))
|
33
|
+
|
34
|
+
;; returns a function with args 'args whose body is 'str. 'str should be a string,
|
35
|
+
;; 'args should correspond to interpolations within 'str
|
36
|
+
;;
|
37
|
+
;; example: (string-eval-fn "hello \~u.firstname" 'u)
|
38
|
+
;; returns (fn (u) (string-pieces "hello " u.firstname))
|
39
|
+
(defmemo string-eval-fn (str args)
|
40
|
+
(eval `(fn ,args
|
41
|
+
,(parse-in-string str))))
|
42
|
+
|
43
|
+
;; assigns 'args respectively to 'arg-names and evals 'str in that context.
|
44
|
+
;; Assumes 'str contains interpolations which reference 'arg-names.
|
45
|
+
;; Useful for evaluating user-supplied strings ; dangerous for the same reason.
|
46
|
+
;;
|
47
|
+
;; example: (string/eval-with-args "\~x + \~y is \~(+ x y)" '(x y) 2 3)
|
48
|
+
;; returns "2 + 3 is 5"
|
49
|
+
;;
|
50
|
+
(def string/eval-with-args (str arg-names . args)
|
51
|
+
(on-err
|
52
|
+
(error (j "error evaluating " (inspect str)
|
53
|
+
"\nwith arg names " (inspect arg-names)
|
54
|
+
"\nand args " (inspect args)))
|
55
|
+
(apply (string-eval-fn str arg-names)
|
56
|
+
args)))
|
@@ -1,5 +1,25 @@
|
|
1
1
|
(chapter-start 'date-time "utilities for retrieving and manipulating dates and times")
|
2
2
|
|
3
|
+
;; return a date for the current day
|
3
4
|
(def today ()
|
4
|
-
; return a date for the current day
|
5
5
|
(date))
|
6
|
+
|
7
|
+
;; return a Time object representing the time 's seconds ago
|
8
|
+
(def seconds-ago (s)
|
9
|
+
(- (time) s))
|
10
|
+
|
11
|
+
(def anniversary/previous (anchor anniv)
|
12
|
+
(let d (date anchor.year
|
13
|
+
anniv.month
|
14
|
+
anniv.day)
|
15
|
+
(if (< d anchor)
|
16
|
+
d
|
17
|
+
d.last-year)))
|
18
|
+
|
19
|
+
(def anniversary/next (anchor anniv)
|
20
|
+
(let d (date anchor.year
|
21
|
+
anniv.month
|
22
|
+
anniv.day)
|
23
|
+
(if (> d anchor)
|
24
|
+
d
|
25
|
+
d.next-year)))
|
@@ -23,76 +23,71 @@
|
|
23
23
|
(intersperse inbetween (cdr things)))
|
24
24
|
things))
|
25
25
|
|
26
|
+
;; expects 'things a list of lists, joins the lists
|
27
|
+
;; returns (a b X c d X e f)
|
28
|
+
;; placing 'inbetween in between each list.
|
29
|
+
;; For example (intersperse-splicing 'X '((a b) (c d) (e f)))
|
26
30
|
(def intersperse-splicing (inbetween things)
|
27
|
-
; expects 'things a list of lists, joins the lists
|
28
|
-
; placing 'inbetween in between each list.
|
29
|
-
; For example (intersperse-splicing 'X '((a b) (c d) (e f)))
|
30
|
-
; returns (a b X c d X e f)
|
31
31
|
(apply joinlists (intersperse (list inbetween) things)))
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
(
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
(f items)
|
47
|
-
items)))
|
33
|
+
;; if 'things is a list, return all the items in the list for which 'f returns non-nil
|
34
|
+
;; otherwise, return 'things if (f things) is non-nil
|
35
|
+
;; otherwise, nil
|
36
|
+
;; note that this preserves improper lists and may return only the lastcdr if all else fails...
|
37
|
+
(def collect (f items)
|
38
|
+
(if (pair? items)
|
39
|
+
(if (f (car items))
|
40
|
+
(cons (car items)
|
41
|
+
(collect f (cdr items)))
|
42
|
+
(collect f (cdr items)))
|
43
|
+
items
|
44
|
+
(if (f items)
|
45
|
+
items)))
|
48
46
|
|
49
47
|
(assign select collect)
|
50
48
|
|
51
|
-
|
52
|
-
|
53
|
-
(collect present? things))
|
49
|
+
;; return a new list containing only 'present? items from the given list
|
50
|
+
(def compact (things) (collect present? things))
|
54
51
|
|
52
|
+
;; return the sum of all non-nil values (consider nil as zero)
|
55
53
|
(def +nz args
|
56
|
-
; return the sum of all non-nil values (consider nil as zero)
|
57
54
|
(apply + (compact args)))
|
58
55
|
|
56
|
+
;; return all the items in 'things for which 'f returns nil
|
59
57
|
(def reject (f things)
|
60
|
-
; return all the items in 'things for which 'f returns nil
|
61
58
|
(collect !f things))
|
62
59
|
|
60
|
+
;; returns the n-th item in the list 'things
|
63
61
|
(def nth (n things)
|
64
|
-
; returns the n-th item in the list 'things
|
65
62
|
(if (eq? n 0)
|
66
63
|
(car things)
|
67
64
|
(nth (- n 1) (cdr things))))
|
68
65
|
|
66
|
+
;; repeatedly assigns an element of 'things to 'var,
|
67
|
+
;; and executes 'body each time
|
69
68
|
(mac each (var things . body)
|
70
|
-
; repeatedly assigns an element of 'things to 'var,
|
71
|
-
; and executes 'body each time
|
72
69
|
(w/uniq (xs c)
|
73
|
-
`(
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
,things)))
|
70
|
+
`(rfnwith ,c (,xs ,things)
|
71
|
+
(if (pair? ,xs)
|
72
|
+
(do (let ,var (car ,xs) ,@body)
|
73
|
+
(,c (cdr ,xs)))))))
|
78
74
|
|
79
75
|
(def reduce (f things)
|
80
|
-
(
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
76
|
+
(rfnwith rd (acc (car things) list (cdr things))
|
77
|
+
(if (pair? list)
|
78
|
+
(rd (f acc (car list))
|
79
|
+
(cdr list))
|
80
|
+
acc)))
|
81
|
+
|
82
|
+
;; t if this is a proper list (last cdr is nil)
|
83
|
+
;; nil otherwise (last cdr is neither cons nor nil)
|
87
84
|
(def proper? (list)
|
88
|
-
; t if this is a proper list (last cdr is nil)
|
89
|
-
; nil otherwise (last cdr is neither cons nor nil)
|
90
85
|
(or (no list)
|
91
86
|
(and (pair? list)
|
92
87
|
(proper? (cdr list)))))
|
93
88
|
|
89
|
+
;; returns the first 'n items in the list 'things
|
94
90
|
(def firstn (n things)
|
95
|
-
; returns the first 'n items in the list 'things
|
96
91
|
(if (eq? n 0) nil
|
97
92
|
things (cons (car things)
|
98
93
|
(firstn (- n 1)
|
@@ -140,34 +135,39 @@
|
|
140
135
|
(detect (curry eq? f)
|
141
136
|
things)))
|
142
137
|
|
138
|
+
;; split things into a list of lists each n long
|
143
139
|
(def tuples (n things)
|
144
|
-
;; split things into a list of lists each n long
|
145
140
|
(rfnwith _ (list things)
|
146
141
|
(if (no list)
|
147
142
|
nil
|
148
143
|
(cons (firstn n list) (_ (nthcdr n list))))))
|
149
144
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
(
|
155
|
-
|
156
|
-
|
157
|
-
stop))))
|
158
|
-
|
159
|
-
(def best (f things)
|
160
|
-
(if (no things)
|
161
|
-
nil
|
162
|
-
(let winner (car things)
|
163
|
-
(each thing (cdr things)
|
164
|
-
(if (f thing winner)
|
165
|
-
(= winner thing)))
|
166
|
-
winner)))
|
145
|
+
;; iterates through 'things pairwise (a,b then b,c then c,d etc), returns whichever is preferred by 'better-p
|
146
|
+
;; example (best > '(3 1 4 1 5 9 2)) returns 9
|
147
|
+
;; "better-p" -> takes two args a and b, return t if a is "better" than b, nil otherwise
|
148
|
+
(def best (better-p things)
|
149
|
+
(reduce (fn (a b)
|
150
|
+
(if (better-p a b) a b))
|
151
|
+
things))
|
167
152
|
|
168
153
|
(def min things (best < things))
|
169
154
|
(def max things (best > things))
|
170
155
|
|
156
|
+
;; returns a function taking two args a and b, that compares attributes of two objects
|
157
|
+
;; 'map-f takes one arg a returning a1
|
158
|
+
;; 'compare-p takes two args a1 and b1, returns t if a1 is "better" than b1
|
159
|
+
;;
|
160
|
+
;; useful in conjunction with 'best : (best (map-compare-f > &size) (list { size 1 } { size 7 } { size 3 })) returns { size 7 }
|
161
|
+
(def map-compare-f (compare-p map-f)
|
162
|
+
(fn (a b) (compare-p (map-f a) (map-f b))))
|
163
|
+
|
164
|
+
;; iterate over 'things, calling 'on-atom for each non-list element, and calling
|
165
|
+
;; 'on-list for each list element.
|
166
|
+
;; 'on-atom takes one parameter, the element in question;
|
167
|
+
;; 'on-list takes two parameters: a function to call for recursing, and the list in question.
|
168
|
+
;; 'on-list should be something like (fn (rec xs) (foo xs) (map rec xs)) to construct a new list,
|
169
|
+
;; or (fn (rec xs) (foo xs) (eachl rec xs)) to iterate without constructing a new list
|
170
|
+
;; see 'list-gsub for an example of constructing a new list using this.
|
171
171
|
(def map-recurse (on-atom on-list things)
|
172
172
|
((afn (xs)
|
173
173
|
(if (pair? xs)
|
@@ -176,8 +176,20 @@
|
|
176
176
|
(on-atom xs)))
|
177
177
|
things))
|
178
178
|
|
179
|
+
;; like map-recurse, but doesn't depend on caller to initiate recursion
|
180
|
+
;; 'on-atom and 'on-list are functions each taking one parameter
|
181
|
+
;; return value is last returned item from on-atom or on-list
|
182
|
+
(def list/traverse (on-atom on-list things)
|
183
|
+
(map-recurse
|
184
|
+
(fn (s)
|
185
|
+
(on-atom s))
|
186
|
+
(fn (rec xs)
|
187
|
+
(on-list xs)
|
188
|
+
(eachl rec xs))
|
189
|
+
things))
|
190
|
+
|
191
|
+
;; recursively replaces 'old with 'new inside 'list
|
179
192
|
(def list-gsub (list old new)
|
180
|
-
; recursively replaces 'old with 'new inside 'list
|
181
193
|
(map-recurse (fn (s) (if (eq? s old) new s))
|
182
194
|
(fn (m things)
|
183
195
|
(if (eq? things old)
|
@@ -186,8 +198,8 @@
|
|
186
198
|
list))
|
187
199
|
|
188
200
|
(def all? (f things)
|
189
|
-
; if 'things is a list, true when
|
190
|
-
; if 'things is an atom, true when non-nil
|
201
|
+
; if 'things is a list, true when f(thing) is non-nil for each thing in things
|
202
|
+
; if 'things is an atom, true when f(things) is non-nil
|
191
203
|
(if (pair? things)
|
192
204
|
(and (f:car things)
|
193
205
|
(or (no:cdr things)
|
@@ -195,8 +207,8 @@
|
|
195
207
|
(f things)))
|
196
208
|
|
197
209
|
(def any? (f things)
|
198
|
-
; if 'things is a list, true when at least one
|
199
|
-
; if 'things is an atom, true when non-nil
|
210
|
+
; if 'things is a list, true when f(thing) is non-nil for at least one thing in things
|
211
|
+
; if 'things is an atom, true when f(thing) is non-nil
|
200
212
|
(if (pair? things)
|
201
213
|
(or (f:car things)
|
202
214
|
(and (cdr things)
|
@@ -204,8 +216,8 @@
|
|
204
216
|
(f things)))
|
205
217
|
|
206
218
|
(def none? (f things)
|
207
|
-
; if 'things is a list, true when
|
208
|
-
; if 'things is an atom, true when nil
|
219
|
+
; if 'things is a list, true when f(thing) is nil for each thing in things
|
220
|
+
; if 'things is an atom, true when f(things) is nil
|
209
221
|
(if (pair? things)
|
210
222
|
(and (no:f:car things)
|
211
223
|
(none? f (cdr things)))
|
@@ -221,3 +233,17 @@
|
|
221
233
|
matchers
|
222
234
|
(matchers things)
|
223
235
|
t))
|
236
|
+
|
237
|
+
; given an arg 'f, invoke 'f with no args
|
238
|
+
(def self-invoke (f) (f))
|
239
|
+
|
240
|
+
;; returns the first element of 'things iff it is the only element of 'things
|
241
|
+
(def list-single-element (things)
|
242
|
+
(if (no (cdr things)) (car things)))
|
243
|
+
|
244
|
+
;; like map, but function 'f takes two arguments: the thing and the 0-based index of the thing
|
245
|
+
(def map-with-index (f things)
|
246
|
+
(let i -1
|
247
|
+
(map (fn (thing)
|
248
|
+
(f thing (++ i)))
|
249
|
+
things)))
|