-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlens.lisp
468 lines (407 loc) · 13.7 KB
/
lens.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
(in-package :data-lens)
(declaim
(inline data-lens:over data-lens:transform-tail
data-lens:applicable-when data-lens:of-min-length
data-lens:on data-lens:over data-lens:slice
data-lens:compress-runs data-lens:combine-matching-lists
data-lens:juxt data-lens:element data-lens:sorted))
(defgeneric functionalize (it)
(:method ((it hash-table))
(lambda (key &optional default)
(gethash key it default)))
(:method ((it vector))
(lambda (idx &optional default)
(let ((present-p (and (>= idx 0)
(< idx (length it)))))
(values (if present-p
(aref it idx)
default)
present-p))))
(:method ((it symbol))
(fdefinition it))
(:method ((it function))
it))
(define-compiler-macro functionalize (&whole whole it)
(typecase it
(cons (destructuring-bind (h . tail) it
(declare (ignore tail))
(case h
(quote it)
(function it)
(t whole))))
(t whole)))
;;; TODO: consider making this wrap defalias?
(defmacro shortcut (name function &body bound-args)
`(eval-when (:load-toplevel :compile-toplevel :execute)
(setf (fdefinition ',name)
(,function ,@bound-args))))
(defmacro defun-ct (name (&rest args) &body body)
`(eval-when (:load-toplevel :compile-toplevel :execute)
(defun ,name ,args
,@body)))
(defmacro let-fn ((&rest bindings) &body body)
(let ((binding-forms (mapcar (lambda (form)
`(,(car form) ,(cadr form)
(funcall ,@(cddr form) ,@(cadr form))))
bindings)))
`(labels ,binding-forms
,@body)))
(defgeneric extract-key (map key)
(:method ((map hash-table) key)
(gethash key map))
(:method ((map list) key)
(typecase (car map)
(cons (cdr (assoc key map :test 'equal)))
(t (loop for (a-key . value) on map by #'cddr
when (equal key a-key) do
(return (car value)))))))
(defun == (target &key (test 'eql))
(lambda (v)
(funcall test target v)))
(defun deduplicate (&optional (test 'eql))
(lambda (it)
(remove-duplicates it :test test)))
(defun cons-new (&key (test 'eql) (key 'identity))
(lambda (acc next)
(if (and acc
(funcall test
(funcall key (car acc))
(funcall key next)))
acc
(cons next acc))))
(defun matching-list-reducer (test acc next)
(if (and acc
(funcall test (caar acc) (car next)))
(cons (cons (caar acc)
(append (cdar acc)
(cdr next)))
(cdr acc))
(cons next acc)))
(defun combine-matching-lists (&key (test 'eql) &allow-other-keys)
(lambda (acc next)
(matching-list-reducer test acc next)))
(defun compress-runs (&key (collector 'cons-new) (test 'eql) (key 'identity))
(lambda (it)
(nreverse
(reduce (funcall collector :test test :key key)
it
:initial-value ()))))
(defun of-length (len)
(lambda (it)
(= (length it)
len)))
(defun of-min-length (len)
(lambda (it)
(>= (length it)
len)))
(defun of-max-length (len)
(lambda (it)
(<= (length it)
len)))
(defun applicable-when (fun test &optional (default nil default-p))
(if default-p
(lambda (it)
(if (funcall test it)
(funcall fun it)
default))
(lambda (it)
(if (funcall test it)
(funcall fun it)
it))))
(defmacro conj (&rest fns)
(let ((dat (gensym "dat")))
`(lambda (,dat)
(declare (ignorable ,dat))
(and ,@(mapcar (lambda (fn)
`(funcall ,fn ,dat))
fns)))))
(defmacro disj (&rest fns)
(let ((dat (gensym "dat")))
`(lambda (,dat)
(declare (ignorable ,dat))
(or ,@(mapcar (lambda (fn)
`(funcall ,fn ,dat))
fns)))))
(defun sorted (comparator &rest r &key key)
(declare (ignore key))
(lambda (it)
(apply #'stable-sort (copy-seq it) comparator r)))
(defun element (num)
(lambda (it)
(elt it num)))
(defun key (key)
(lambda (map)
(declare (dynamic-extent map))
(extract-key map key)))
(defun keys (key &rest keys)
(lambda (map)
(loop for key in (cons key keys)
for cur = (extract-key map key) then (extract-key cur key)
finally (return cur))))
(defun regex-match (regex)
(lambda (data)
(cl-ppcre:scan-to-strings regex data)))
(defun include (pred)
(lambda (seq)
(remove-if-not pred seq)))
(defun exclude (pred)
(lambda (seq)
(remove-if pred seq)))
(defun pick (selector)
(lambda (seq)
(map 'list selector seq)))
(defun tap (cb)
(lambda (it)
(prog1 it
(funcall cb it))))
(defun slice (start &optional end)
(lambda (it)
(subseq it start end)))
(defun update (thing fun &rest args)
(apply fun thing args))
(define-modify-macro updatef (fun &rest args)
update)
(defun suffixp (suffix &key (test 'eql test-p))
(lambda (it)
(if test-p
(alexandria:ends-with-subseq suffix
it
:test test)
(alexandria:ends-with-subseq suffix
it))))
(defun of-type (type)
(lambda (it)
(when (typep it type)
it)))
(defun transform-head (fun)
(lambda (it)
(typecase it
(list (list* (funcall fun (car it))
(cdr it)))
(vector (let ((result (copy-seq it)))
(prog1 result
(updatef (elt result 0) fun)))))))
(defun transform-tail (fun)
(lambda (it)
(typecase it
(list (list* (car it)
(funcall fun (cdr it))))
(vector (let ((result (copy-seq it)))
(prog1 result
(updatef (subseq result 1)
fun)))))))
(defun splice-elt (elt fun)
(lambda (it)
(append (subseq it 0 elt)
(funcall fun (nth elt it))
(subseq it (1+ elt)))))
(defun transform-elt (elt fun)
(lambda (it)
(concatenate (type-of it)
(subseq it 0 elt)
(list (funcall fun (elt it elt)))
(subseq it (1+ elt)))))
(defun key-transform (fun key-get key-set)
(lambda (it)
(let ((key-val (funcall key-get it)))
(funcall key-set
(funcall fun key-val)))))
(defun juxt (fun1 &rest r)
(lambda (&rest args)
(list* (apply fun1 args)
(when r
(mapcar (lambda (f)
(apply f args))
r)))))
(defun delay ()
"Return a function that always returns the last thing it was called with"
(let ((result nil))
(lambda (v)
(prog1 result
(setf result v)))))
(defun =>> (fun1 fun2)
(lambda (i)
(prog1 (funcall fun1 i)
(funcall fun2))))
(defun derive (diff-fun &key (key #'identity))
(lambda (seq)
(typecase seq
(list (cons (cons nil (car seq))
(mapcar (lambda (next cur)
(cons (funcall diff-fun
(funcall key next)
(funcall key cur))
next))
(cdr seq)
seq)))
(vector (coerce (loop for cur = nil then next
for next across seq
if cur
collect (cons (funcall diff-fun
(funcall key next)
(funcall key cur))
cur)
else collect (cons nil next))
'vector)))))
(defun inc (inc)
(declare (optimize (speed 3)))
(lambda (base)
(+ base inc)))
(defun cumsum
(&key (add-fun #'+) (key #'identity) (combine (lambda (x y) y x)) (zero 0))
(lambda (seq)
(nreverse
(reduce (lambda (accum next)
(let ((key-val (funcall key next))
(old-val (if accum
(funcall key (car accum))
zero)))
(cons (funcall combine
(funcall add-fun old-val key-val)
next)
accum)))
seq
:initial-value ()))))
(defun over (fun &rest funs)
(let* ((fun (functionalize fun))
(rt-pos (position :result-type funs))
(result-type (cond
((null rt-pos) 'list)
((>= (1+ rt-pos)
(length funs))
(error "invalid result-type"))
(t
(elt funs (1+ rt-pos)))))
(funs (if rt-pos
(append (mapcar #'functionalize
(subseq funs 0 rt-pos))
(mapcar #'functionalize
(subseq funs (+ rt-pos 2))))
(mapcar #'functionalize funs)))
(combined-fun (if funs
(apply #'alexandria:compose fun funs)
fun)))
(lambda (seq &rest seqs)
(if seqs
(apply #'map result-type combined-fun seq seqs)
(map result-type combined-fun seq)))))
(defun denest (&key (result-type 'list))
(lambda (seq)
(apply #'concatenate result-type
seq)))
(defun transform (arg &rest args)
(if args
(lambda (fn)
(apply fn arg args))
(lambda (fn)
(funcall fn arg))))
(defmacro calling (fun &rest args)
(alexandria:with-gensyms (first-arg)
`(lambda (,first-arg)
(funcall (functionalize ,fun) ,first-arg ,@args))))
(defmacro calling* (fun &rest args)
(alexandria:with-gensyms (last-arg)
`(lambda (,last-arg)
(funcall (functionalize ,fun) ,@args ,last-arg))))
(defmacro applying (fun &rest args)
(alexandria:with-gensyms (seq fsym)
`(let ((,fsym (functionalize ,fun)))
(lambda (,seq)
(apply ,fsym ,@args ,seq)))))
(defun on (fun key)
"Transform arguments with KEY and then apply FUN
> (eql (funcall (on 'equal 'car)
> '(\"a\" 1 2)
> '(\"a\" 2 e))
> t)"
(let ((fun (functionalize fun))
(key (functionalize key)))
(lambda (&rest its)
(apply fun (mapcar key its)))))
(defun filler (length1 length2 fill-value)
(if (< length1 length2)
(make-sequence 'vector (- length2 length1) :initial-element fill-value)
#()))
(defun zipping (result-type &key (fill-value nil fill-value-p))
(lambda (seq1 seq2)
(let ((length1 (when fill-value-p (length seq1)))
(length2 (when fill-value-p (length seq2))))
(let ((seq1 (if fill-value-p
(concatenate result-type
seq1
(filler length1 length2 fill-value))
seq1))
(seq2 (if fill-value-p
(concatenate result-type
seq2
(filler length2 length1 fill-value))
seq2)))
(map result-type #'list
seq1 seq2)))))
(defun maximizing (relation measure)
(lambda (it)
(let ((it-length (length it)))
(when (> it-length 0)
(values-list
(reduce (lambda (|arg1764| |arg1765|)
(destructuring-bind (cur-max max-idx) |arg1764|
(destructuring-bind (next next-idx) |arg1765|
(if (funcall relation (funcall measure cur-max) (funcall measure next))
(list next next-idx)
(list cur-max max-idx)))))
(funcall (zipping 'vector)
it
(alexandria:iota it-length))))))))
(defun group-by (fn &key (test 'equal))
(lambda (seq)
(let ((groups (make-hash-table :test test)))
(map nil
(lambda (it)
(push it
(gethash (funcall fn it)
groups)))
seq)
(mapcar (lambda (it)
(cons (car it)
(reverse (cdr it))))
(alexandria:hash-table-alist groups)))))
(defun x-group (fn)
(lambda (groups)
(loop for (key . group) in groups
collect (funcall fn key group))))
(defun hash-join (probe join-fn &key (test 'eql) (key 'car))
(let* ((lookup (make-hash-table :test test :size (length probe)))
(lookup-fn (functionalize lookup)))
(map nil
(lambda (it)
(setf (gethash (funcall key it)
lookup)
it))
probe)
(lambda (collection)
(map (etypecase collection
(list 'list)
(vector 'vector)
(sequence 'list))
(lambda (it)
(let* ((key-value (funcall key it))
(matching-probe (funcall lookup-fn key-value)))
(funcall join-fn it matching-probe)))
collection))))
#+nil
(defmacro <> (arity &rest funs)
(let ((arg-syms (loop repeat arity collect (gensym))))
`(lambda (,@arg-syms)
(declare (dynamic-extent ,@arg-syms))
,(fw.lu:rollup-list (mapcar (lambda (x)
(etypecase x
(list `(funcall ,x))
(symbol (list x))))
funs)
arg-syms))))
(defmacro <>1 (&rest funs)
`(alexandria:compose ,@funs))
(defmacro • (&rest funs)
`(alexandria:compose ,@funs))
(defmacro ∘ (&rest funs)
`(alexandria:compose ,@funs))