nydp 0.4.2 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +44 -0
- data/lib/lisp/core-010-precompile.nydp +13 -16
- data/lib/lisp/core-012-utils.nydp +3 -2
- data/lib/lisp/core-015-documentation.nydp +54 -23
- data/lib/lisp/core-017-builtin-dox.nydp +14 -12
- data/lib/lisp/core-020-utils.nydp +5 -5
- data/lib/lisp/core-030-syntax.nydp +166 -72
- data/lib/lisp/core-035-flow-control.nydp +38 -11
- data/lib/lisp/core-037-list-utils.nydp +12 -0
- data/lib/lisp/core-039-module.nydp +24 -0
- data/lib/lisp/core-040-utils.nydp +32 -12
- data/lib/lisp/core-041-string-utils.nydp +25 -1
- data/lib/lisp/core-042-date-utils.nydp +21 -1
- data/lib/lisp/core-043-list-utils.nydp +96 -64
- data/lib/lisp/core-070-prefix-list.nydp +1 -1
- data/lib/lisp/core-080-pretty-print.nydp +57 -17
- data/lib/lisp/core-090-hook.nydp +35 -1
- data/lib/lisp/core-100-utils.nydp +82 -2
- data/lib/lisp/core-110-hash-utils.nydp +56 -2
- data/lib/lisp/core-120-settings.nydp +16 -5
- data/lib/lisp/core-130-validations.nydp +51 -0
- data/lib/lisp/core-900-benchmarking.nydp +78 -20
- 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/date-examples.nydp +54 -1
- data/lib/lisp/tests/destructuring-examples.nydp +46 -14
- data/lib/lisp/tests/detect-examples.nydp +12 -0
- data/lib/lisp/tests/dp-examples.nydp +24 -0
- data/lib/lisp/tests/each-tests.nydp +5 -0
- data/lib/lisp/tests/empty-examples.nydp +1 -1
- data/lib/lisp/tests/error-tests.nydp +4 -4
- data/lib/lisp/tests/explain-mac-examples.nydp +1 -1
- data/lib/lisp/tests/filter-forms-examples.nydp +15 -0
- data/lib/lisp/tests/hash-examples.nydp +25 -1
- 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/module-examples.nydp +10 -0
- data/lib/lisp/tests/multi-assign-examples.nydp +6 -0
- data/lib/lisp/tests/parser-tests.nydp +25 -0
- data/lib/lisp/tests/pretty-print-tests.nydp +17 -14
- data/lib/lisp/tests/set-difference-examples.nydp +8 -0
- data/lib/lisp/tests/settings-examples.nydp +17 -1
- data/lib/lisp/tests/string-tests.nydp +70 -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/nydp.rb +10 -3
- 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/date.rb +15 -1
- data/lib/nydp/builtin/error.rb +1 -1
- data/lib/nydp/builtin/hash.rb +24 -1
- data/lib/nydp/builtin/inspect.rb +1 -1
- data/lib/nydp/builtin/plus.rb +10 -2
- data/lib/nydp/builtin/random_string.rb +2 -2
- data/lib/nydp/builtin/{car.rb → regexp.rb} +2 -2
- data/lib/nydp/builtin/ruby_wrap.rb +72 -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 +3 -3
- data/lib/nydp/builtin/string_split.rb +4 -3
- 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 +36 -28
- data/lib/nydp/core_ext.rb +21 -5
- data/lib/nydp/date.rb +26 -18
- data/lib/nydp/function_invocation.rb +34 -26
- data/lib/nydp/helper.rb +35 -3
- data/lib/nydp/interpreted_function.rb +68 -40
- data/lib/nydp/literal.rb +1 -1
- data/lib/nydp/pair.rb +22 -5
- data/lib/nydp/parser.rb +11 -7
- data/lib/nydp/string_atom.rb +3 -4
- data/lib/nydp/symbol_lookup.rb +7 -7
- data/lib/nydp/tokeniser.rb +2 -2
- data/lib/nydp/truth.rb +10 -10
- data/lib/nydp/version.rb +1 -1
- data/lib/nydp/vm.rb +7 -0
- data/nydp.gemspec +2 -4
- data/spec/date_spec.rb +93 -0
- 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/pair_spec.rb +3 -1
- data/spec/parser_spec.rb +31 -20
- data/spec/rand_spec.rb +3 -3
- data/spec/spec_helper.rb +10 -1
- metadata +24 -37
- data/lib/nydp/builtin/cdr.rb +0 -7
- data/lib/nydp/builtin/cons.rb +0 -9
@@ -13,9 +13,9 @@
|
|
13
13
|
`(ensuring (fn () ,protection)
|
14
14
|
(fn () ,@body)))
|
15
15
|
|
16
|
+
;; tests 'test, as long as 'test is non-nil,
|
17
|
+
;; repeatedly executes 'body
|
16
18
|
(mac while (test . body)
|
17
|
-
; tests 'test, as long as 'test is non-nil,
|
18
|
-
; repeatedly executes 'body
|
19
19
|
(w/uniq (rfname pred)
|
20
20
|
`(rfnwith ,rfname (,pred ,test)
|
21
21
|
(when ,pred
|
@@ -40,12 +40,21 @@
|
|
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.
|
@@ -56,13 +65,31 @@
|
|
56
65
|
(let ,v (hash-get ,h ,k)
|
57
66
|
(or ,v (returnlet ,v ,val (hash-set ,h ,k ,v)))))))
|
58
67
|
|
68
|
+
;; same as 'def, but caches the result, keyed on args, so for a given set of args the result
|
69
|
+
;; is only ever calculated once
|
70
|
+
;;
|
71
|
+
;; WARNING: in current incarnation, won't work with destructuring args
|
59
72
|
(mac defmemo (name args . body)
|
60
|
-
|
61
|
-
; is only ever calculated once
|
62
|
-
(let forms (filter-forms (build-def-hash (hash)) body)
|
73
|
+
(let forms (filter-forms (build-def-hash) body)
|
63
74
|
(w/uniq h
|
64
75
|
`(let ,h (hash)
|
65
76
|
(def ,name ,args
|
66
77
|
,@(map (fn (c) (cons 'comment c)) forms.comment)
|
67
78
|
,@(map (fn (c) (cons 'chapter c)) forms.chapter)
|
68
79
|
(cache-get ,h (list ,@args) (do ,@(hash-get forms nil))))))))
|
80
|
+
|
81
|
+
;; memoises a function expression
|
82
|
+
;; args: the function arguments
|
83
|
+
;; body: a list of function body expressions
|
84
|
+
;; next: a function to assemble a function expression from 'args and 'body
|
85
|
+
;; returns whatever 'next returns, where 'body is memoised based on the value of 'args
|
86
|
+
(def memoise (args body next)
|
87
|
+
(let (memo newbody) (filter-remove '#memoise body)
|
88
|
+
(if memo
|
89
|
+
(w/uniq h
|
90
|
+
`(let ,h (hash) ,(next args `((cache-get ,h (list ,@args) (do ,@newbody))))))
|
91
|
+
(next args body))))
|
92
|
+
|
93
|
+
(assign fun/expanders
|
94
|
+
(cons
|
95
|
+
(cons 'memoise memoise) fun/expanders))
|
@@ -13,6 +13,17 @@
|
|
13
13
|
(f (car things))
|
14
14
|
(eachl f (cdr things))))
|
15
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
|
+
|
16
27
|
; invokes 'f for each element of 'things, last element processed first
|
17
28
|
; ( "r" in "eachr" = "rightmost first" )
|
18
29
|
(def eachr (f things)
|
@@ -29,6 +40,7 @@
|
|
29
40
|
(rfnwith flattenize (x things)
|
30
41
|
(if (pair? x)
|
31
42
|
(eachr flattenize x)
|
43
|
+
x
|
32
44
|
(push (f x) acc)))
|
33
45
|
acc))
|
34
46
|
|
@@ -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,21 +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
|
-
|
21
|
+
;; return a function that always returns 'arg, similar to K in SKI calculus
|
23
22
|
(defmemo k (arg) (fn nil arg))
|
24
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
|
25
27
|
(def len (things)
|
26
|
-
; return the length of 'things where 'things may be nil, a string, list or hash
|
27
|
-
; length of nil is zero, length of hash is number of keys, length of string
|
28
|
-
; is number of characters, length of list is number of direct items - no recursive counting
|
29
28
|
(chapter list-manipulation)
|
30
29
|
(chapter string-manipulation)
|
31
30
|
(chapter hash-manipulation)
|
@@ -37,8 +36,8 @@
|
|
37
36
|
|
38
37
|
(assign dynamics (hash))
|
39
38
|
|
39
|
+
;; creates a dynamic variable.
|
40
40
|
(mac dynamic (name initial)
|
41
|
-
; creates a dynamic variable.
|
42
41
|
(let with-mac-name (sym:+ "w/" name)
|
43
42
|
(w/uniq prev
|
44
43
|
`(do
|
@@ -53,10 +52,10 @@
|
|
53
52
|
(def ,name () (hash-get (thread-locals) ',name))))))
|
54
53
|
|
55
54
|
|
56
|
-
|
55
|
+
;; overrides 'privately defined earlier in documentation manager
|
57
56
|
(dynamic privately)
|
58
57
|
|
59
|
-
|
58
|
+
;; suppress documentation of anything defined in 'body
|
60
59
|
(mac in-private body
|
61
60
|
`(w/privately t ,@body))
|
62
61
|
|
@@ -91,3 +90,24 @@
|
|
91
90
|
(chapter string-manipulation)
|
92
91
|
(chapter hash-manipulation)
|
93
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))
|
104
|
+
|
105
|
+
;; returns a function that returns a number sequence. Example:
|
106
|
+
;; (let c (counter)
|
107
|
+
;; (p (c)) ;;=> 0
|
108
|
+
;; (p (c)) ;;=> 1
|
109
|
+
;; (p (c))) ;;=> 2
|
110
|
+
;;
|
111
|
+
(def counter ()
|
112
|
+
(let i -1
|
113
|
+
(fn () (++ i))))
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
; return a new string with leading and trailing whitespace removed
|
4
4
|
(def string-strip (txt)
|
5
|
-
(string-replace "(
|
5
|
+
(string-replace "(\\A\\s+|\\s+\\z)" "" txt))
|
6
6
|
|
7
7
|
(def joinstr (txt . things)
|
8
8
|
; flatten 'things into a single list (ie unnest lists)
|
@@ -30,3 +30,27 @@
|
|
30
30
|
; return the first 'length chars of string 'str
|
31
31
|
(def string-truncate (str length)
|
32
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,72 @@
|
|
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
|
|
63
|
-
(def
|
64
|
-
; returns the n-th item in the list 'things
|
65
|
-
(if (eq? n 0)
|
66
|
-
(car things)
|
67
|
-
(nth (- n 1) (cdr things))))
|
68
|
-
|
69
|
-
(mac each (var things . body)
|
70
|
-
; repeatedly assigns an element of 'things to 'var,
|
71
|
-
; and executes 'body each time
|
60
|
+
(def each/build-expression (var things body othervars otherparams)
|
72
61
|
(w/uniq (xs c)
|
73
|
-
`(
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
62
|
+
`(rfnwith ,c (,xs ,things ,@othervars)
|
63
|
+
(if (pair? ,xs)
|
64
|
+
(let ,var (car ,xs)
|
65
|
+
,@body
|
66
|
+
(,c (cdr ,xs) ,@otherparams))))))
|
67
|
+
|
68
|
+
;; repeatedly assigns an element of 'things to 'var,
|
69
|
+
;; and executes 'body each time
|
70
|
+
(mac each (var things . body)
|
71
|
+
(each/build-expression var things body))
|
78
72
|
|
79
|
-
(
|
80
|
-
((
|
81
|
-
(if (pair? list)
|
82
|
-
(rd (f acc (car list))
|
83
|
-
(cdr list))
|
84
|
-
acc))
|
85
|
-
(car things) (cdr things)))
|
73
|
+
(mac each-with-index (ivar var things . body)
|
74
|
+
(each/build-expression var things body `(,ivar 0) `((+ ,ivar 1))))
|
86
75
|
|
76
|
+
(def reduce (f things)
|
77
|
+
(rfnwith rd (acc (car things) list (cdr things))
|
78
|
+
(if (pair? list)
|
79
|
+
(rd (f acc (car list))
|
80
|
+
(cdr list))
|
81
|
+
acc)))
|
82
|
+
|
83
|
+
;; t if this is a proper list (last cdr is nil)
|
84
|
+
;; nil otherwise (last cdr is neither cons nor nil)
|
87
85
|
(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
86
|
(or (no list)
|
91
87
|
(and (pair? list)
|
92
88
|
(proper? (cdr list)))))
|
93
89
|
|
90
|
+
;; returns the first 'n items in the list 'things
|
94
91
|
(def firstn (n things)
|
95
|
-
; returns the first 'n items in the list 'things
|
96
92
|
(if (eq? n 0) nil
|
97
93
|
things (cons (car things)
|
98
94
|
(firstn (- n 1)
|
@@ -140,25 +136,39 @@
|
|
140
136
|
(detect (curry eq? f)
|
141
137
|
things)))
|
142
138
|
|
139
|
+
;; split things into a list of lists each n long
|
143
140
|
(def tuples (n things)
|
144
|
-
;; split things into a list of lists each n long
|
145
141
|
(rfnwith _ (list things)
|
146
142
|
(if (no list)
|
147
143
|
nil
|
148
144
|
(cons (firstn n list) (_ (nthcdr n list))))))
|
149
145
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
(if (
|
156
|
-
|
157
|
-
winner)))
|
146
|
+
;; iterates through 'things pairwise (a,b then b,c then c,d etc), returns whichever is preferred by 'better-p
|
147
|
+
;; example (best > '(3 1 4 1 5 9 2)) returns 9
|
148
|
+
;; "better-p" -> takes two args a and b, return t if a is "better" than b, nil otherwise
|
149
|
+
(def best (better-p things)
|
150
|
+
(reduce (fn (a b)
|
151
|
+
(if (better-p a b) a b))
|
152
|
+
things))
|
158
153
|
|
159
154
|
(def min things (best < things))
|
160
155
|
(def max things (best > things))
|
161
156
|
|
157
|
+
;; returns a function taking two args a and b, that compares attributes of two objects
|
158
|
+
;; 'map-f takes one arg a returning a1
|
159
|
+
;; 'compare-p takes two args a1 and b1, returns t if a1 is "better" than b1
|
160
|
+
;;
|
161
|
+
;; useful in conjunction with 'best : (best (map-compare-f > &size) (list { size 1 } { size 7 } { size 3 })) returns { size 7 }
|
162
|
+
(def map-compare-f (compare-p map-f)
|
163
|
+
(fn (a b) (compare-p (map-f a) (map-f b))))
|
164
|
+
|
165
|
+
;; iterate over 'things, calling 'on-atom for each non-list element, and calling
|
166
|
+
;; 'on-list for each list element.
|
167
|
+
;; 'on-atom takes one parameter, the element in question;
|
168
|
+
;; 'on-list takes two parameters: a function to call for recursing, and the list in question.
|
169
|
+
;; 'on-list should be something like (fn (rec xs) (foo xs) (map rec xs)) to construct a new list,
|
170
|
+
;; or (fn (rec xs) (foo xs) (eachl rec xs)) to iterate without constructing a new list
|
171
|
+
;; see 'list-gsub for an example of constructing a new list using this.
|
162
172
|
(def map-recurse (on-atom on-list things)
|
163
173
|
((afn (xs)
|
164
174
|
(if (pair? xs)
|
@@ -167,8 +177,20 @@
|
|
167
177
|
(on-atom xs)))
|
168
178
|
things))
|
169
179
|
|
180
|
+
;; like map-recurse, but doesn't depend on caller to initiate recursion
|
181
|
+
;; 'on-atom and 'on-list are functions each taking one parameter
|
182
|
+
;; return value is last returned item from on-atom or on-list
|
183
|
+
(def list/traverse (on-atom on-list things)
|
184
|
+
(map-recurse
|
185
|
+
(fn (s)
|
186
|
+
(on-atom s))
|
187
|
+
(fn (rec xs)
|
188
|
+
(on-list xs)
|
189
|
+
(eachl rec xs))
|
190
|
+
things))
|
191
|
+
|
192
|
+
;; recursively replaces 'old with 'new inside 'list
|
170
193
|
(def list-gsub (list old new)
|
171
|
-
; recursively replaces 'old with 'new inside 'list
|
172
194
|
(map-recurse (fn (s) (if (eq? s old) new s))
|
173
195
|
(fn (m things)
|
174
196
|
(if (eq? things old)
|
@@ -177,8 +199,8 @@
|
|
177
199
|
list))
|
178
200
|
|
179
201
|
(def all? (f things)
|
180
|
-
; if 'things is a list, true when
|
181
|
-
; if 'things is an atom, true when non-nil
|
202
|
+
; if 'things is a list, true when f(thing) is non-nil for each thing in things
|
203
|
+
; if 'things is an atom, true when f(things) is non-nil
|
182
204
|
(if (pair? things)
|
183
205
|
(and (f:car things)
|
184
206
|
(or (no:cdr things)
|
@@ -186,8 +208,8 @@
|
|
186
208
|
(f things)))
|
187
209
|
|
188
210
|
(def any? (f things)
|
189
|
-
; if 'things is a list, true when at least one
|
190
|
-
; if 'things is an atom, true when non-nil
|
211
|
+
; if 'things is a list, true when f(thing) is non-nil for at least one thing in things
|
212
|
+
; if 'things is an atom, true when f(thing) is non-nil
|
191
213
|
(if (pair? things)
|
192
214
|
(or (f:car things)
|
193
215
|
(and (cdr things)
|
@@ -195,8 +217,8 @@
|
|
195
217
|
(f things)))
|
196
218
|
|
197
219
|
(def none? (f things)
|
198
|
-
; if 'things is a list, true when
|
199
|
-
; if 'things is an atom, true when nil
|
220
|
+
; if 'things is a list, true when f(thing) is nil for each thing in things
|
221
|
+
; if 'things is an atom, true when f(things) is nil
|
200
222
|
(if (pair? things)
|
201
223
|
(and (no:f:car things)
|
202
224
|
(none? f (cdr things)))
|
@@ -215,3 +237,13 @@
|
|
215
237
|
|
216
238
|
; given an arg 'f, invoke 'f with no args
|
217
239
|
(def self-invoke (f) (f))
|
240
|
+
|
241
|
+
;; returns the first element of 'things iff it is the only element of 'things
|
242
|
+
(def list-single-element (things)
|
243
|
+
(if (no (cdr things)) (car things)))
|
244
|
+
|
245
|
+
;; like map, but function 'f takes two arguments: the thing and the 0-based index of the thing
|
246
|
+
(def map-with-index (f things)
|
247
|
+
(let c (counter)
|
248
|
+
(map (fn (thing) (f thing (c)))
|
249
|
+
things)))
|