-
Notifications
You must be signed in to change notification settings - Fork 3
/
sequence-utils.dylan
590 lines (538 loc) · 16.3 KB
/
sequence-utils.dylan
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
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
module: Sequence-Utilities
author: Matthias Hölzl ([email protected])
copyright: see below
version: 0.1 10 Apr 2004
synopsis: This Module implements some useful methods on sequences.
// Copyright.
// =========
// Copyright (C) 1998-2004 Matthias Hölzl.
// Copyright (C) 1998 Way Forward Technologies.
// Use and copying of this software and preparation of derivative
// works based on this software are permitted, including commercial
// use, provided that the following conditions are observed:
//
// 1. This copyright notice must be retained in full on any copies
// and on appropriate parts of any derivative works. (Other names
// and years may be added, so long as no existing ones are removed.)
//
// This software is made available "as is". Neither the authors nor
// Carnegie Mellon University make any warranty about the software,
// its performance, or its conformity to any specification.
//
// Bug reports, questions, comments, and suggestions should be sent by
// E-mail to the Internet address "[email protected]".
// If you need to receive this library under another license contact
// the author ([email protected]).
// PUSH! -- add an element to the front of a list.
//
define macro push!
{ push!(?location:expression, ?value:expression) }
=> { ?location := pair(?value, ?location) }
end macro push!;
// POP! -- remove the first element of a list.
//
define macro pop!
{ pop!(?location:expression) }
=> { let tmp = head(?location);
?location := tail(?location);
tmp }
end macro pop!;
// PAIR? -- check whether ARG is a pair.
//
define method pair? (arg :: <pair>)
#t;
end method pair?;
define method pair? (arg :: <object>)
#f;
end method pair?;
// NULL? -- check whether ARG is the empty list.
//
// NULL?(lst) iff ~PAIR?(lst) holds for all lists, but not for other
// values.
//
define method null? (arg :: <empty-list>)
#t;
end method null?;
define method null? (arg :: <object>)
#f;
end method null?;
// LIST? -- check whether ARG is a list.
//
define method list? (arg :: <list>)
#t;
end method list?;
define method list? (arg :: <object>)
#f;
end method list?;
// XPAIR -- occasionally useful as a value to be passed to a fold or
// other higher-order procedure.
//
define function xpair (list :: <list>, elt) => new-list :: <list>;
pair(elt, list);
end function xpair;
// TABULATE -- make a sequence of type TYPE whose i-th element is
// FUNC(i) for 0 <= i < LENGTH. TYPE must be a subtype of
// <MUTABLE-SEQUENCE>.
//
define function tabulate
(length :: <integer>, func :: <function>,
#key type = <list>)
if (type = <list>)
list-tabulate(length, func);
else
let res = make(type, size: length);
for (i from 0 below length)
res[i] := func(i);
end for;
res;
end if;
end function tabulate;
// LIST-TABULATE -- make a list of length LENGTH. The i-th element is
// FUNC(i) for 0 <= i < LENGTH.
//.
define function list-tabulate
(length :: <integer>, func :: <function>)
=> new-list :: <list>;
let res = #();
for (i from length - 1 to 0 by -1)
res := pair(func(i), res);
end for;
res;
end function list-tabulate;
// LIST* -- like LIST, but cons the first elements onto the last
// element of REST.
//
define function list* (#rest rest) => new-list :: <list>;
let res = rest[rest.size - 1];
for (i from rest.size - 2 to 0 by -1)
res := pair(rest[i], res);
end for;
res;
end function list*;
// TAKE -- if K > 0 return a new sequence consisting of the first K
// elements of SEQ, otherwise return a new sequence consisting of the
// last K elements of SEQ.
//
define open generic take (collection, k :: <integer>);
define method take (seq :: <sequence>, k :: <integer>)
=> new-sequence :: <sequence>;
if (negative?(k))
copy-sequence(seq, start: seq.size + k);
else
copy-sequence(seq, end: k + 1);
end if;
end method take;
// DROP -- if K > 0 return a new sequence consisting of all but the
// first K elements of SEQ, otherwise return a new sequence consisting
// of all but the K last elements of SEQ.
//
define open generic drop (collection, k :: <integer>);
define method drop (seq :: <sequence>, k :: <integer>)
=> new-sequence :: <sequence>;
if (negative?(k))
copy-sequence(seq, end: seq.size + k);
else
copy-sequence(seq, start: k);
end if;
end method drop;
// LAST-PAIR -- return the last pair in a non-empty list.
//
define function last-pair (lst :: <pair>) => last-pair :: <pair>;
if (empty?(tail(lst)))
lst;
else
last-pair(tail(lst));
end if;
end function last-pair;
// REVERSE-APPEND -- append the reversed REVERSED-HEAD to the TAIL.
//
define open generic reverse-append
(reversed-head :: <sequence>, tail :: <sequence>)
=> new-sequence :: <sequence>;
define method reverse-append
(rev-head :: <sequence>, tail :: <sequence>)
=> new-sequence :: <sequence>;
concatenate(reverse(rev-head), tail);
end method reverse-append;
define method reverse-append
(rev-head :: <list>, tail :: <list>)
=> list :: <list>;
foldl(pair, tail, rev-head);
end method reverse-append;
// UNFOLD -- basic list constructor; see the implementation for its
// recursion scheme.
//
define function unfold
(pred :: <function>, f :: <function>, g :: <function>, seed)
=> new-list :: <list>;
local method recur (seed)
if (pred(seed))
#();
else
pair(f(seed), recur(g(seed)));
end if;
end method;
recur(seed);
end function unfold;
// UNFOLD/TAIL
//
define function unfold/tail
(pred :: <function>, f :: <function>, g :: <function>,
e :: <function>, seed)
=> new-list :: <list>;
local method recur (seed)
if (pred(seed))
e(seed);
else
pair(f(seed), recur(g(seed)));
end if;
end method;
recur(seed);
end function unfold/tail;
// FOLDL -- if LST is #(e1, ..., en) then foldl(lst) returns
// cons(en, cons(..., cons(e2, cons(e1, nil) ...))).
//
// Should we define FOLDL, FOLDR, etc. for arbitrary sequences?
//
define function foldl (cons :: <function>, nil, lst :: <list>)
local method recur (lst :: <list>, acc)
if (pair?(lst))
recur(tail(lst), cons(head(lst), acc));
else
acc;
end if;
end method recur;
recur(lst, nil);
end function foldl;
// FOLDR -- if LST is #(e1, ..., en) then foldr(lst) returns
// cons(e1, cons(..., cons(en, nil) ...)).
// For example foldr(pair, #(), lst) copies lst.
//
define function foldr (cons :: <function>, nil, lst :: <list>)
local method recur (lst :: <list>)
if (pair?(lst))
cons(head(lst), recur(tail(lst)));
else
nil;
end if;
end method;
recur(lst);
end function foldr;
// PAIR-FOLDL -- analogous to FOLDL, but applied to successive
// sublists.
//
define function pair-foldl (cons :: <function>, nil, lst :: <list>)
local method recur (lst :: <list>, acc)
if (pair?(lst))
recur(tail(lst), cons(lst, acc));
else
acc;
end if;
end method;
recur(lst, nil);
end function pair-foldl;
// PAIR-FOLDR -- analogous to FOLDR, but applied to successive
// sublists.
//
define function pair-foldr (cons :: <function>, nil, lst :: <list>)
local method recur (lst :: <list>)
if (pair?(lst))
cons(lst, recur(tail(lst)));
else
nil;
end if;
end method;
recur(lst);
end function pair-foldr;
// REDUCE-L -- a variant of FOLDL. RZERO should be a "right zero" of
// the two-argument procedure F, that is, for any value X acceptable
// to F,
// f(x, rzero) = x
// REDUCE-L has the following definition:
// If LIS = (), return LZERO.
// If LIS = (x), return X.
// Otherwise, return (foldl f (car x) (cdr x)). Note that RZERO is
// used *only* in the empty-list case. You typically use REDUCE-L when
// applying F is expensive and you'd like to avoid the extra
// application incurred when FOLDL applies F to the head of LIS and
// the zero -- for example, if F involves searching a file directory
// or performing a database query, this can be significant. In
// general, however, FOLDL is useful in many contexts where REDUCE-L is
// not (consider the examples given in the FOLDL definition -- only
// one of the five folds uses a function with a right zero. The other
// four may not be performed with REDUCE-L).
//
define function reduce-l (cons :: <function>, nil, lst :: <list>)
if (pair?(lst))
foldl(cons, head(lst), tail(lst));
else
nil;
end if;
end function reduce-l;
// REDUCE-R -- like foldr, but NIL is only used in the empty list
// case.
//
define function reduce-r (cons :: <function>, nil, lst :: <list>)
if (pair?(lst))
local method recur(hd, lst)
if (pair?(lst))
cons(hd, recur(head(lst), tail(lst)));
else
hd;
end if;
end method;
recur(head(lst), tail(lst));
else
nil;
end if;
end function reduce-r;
// HEADS -- returns a list of all the heads of members of LISTS.
//
define function heads (lists :: <list>) => new-list :: <list>;
local method recur (lst :: <list>)
if (pair?(lst))
pair(head(head(lst)), recur(tail(lst)));
else
#();
end if;
end method;
recur(lists);
end function heads;
// TAILS -- returns a list of all the tails of members of LIST.
//
define function tails (lists :: <list>)
local method recur (lst :: <list>)
if (pair?(lst))
pair(tail(head(lst)), recur(tail(lst)));
else
#();
end if;
end method;
recur(lists);
end function tails;
// CONCATENATE-MAP -- concatenates SEQ and all members of SEQS
// together and maps FUNC over the resulting list. The order of
// function applications is unspecified.
//
define method concatenate-map
(func :: <function>, seq :: <sequence>, #rest seqs)
=> new-sequence :: <sequence>;
map(func, apply(concatenate, seq, seqs));
end method concatenate-map;
// CONCATENATE-MAP -- specialized version for lists.
//
define method concatenate-map
(func :: <function>, list :: <list>, #rest lists)
=> new-list :: <list>;
if (empty?(lists)) // fast path
foldr(method (elt, acc) concatenate(func(list), acc) end,
#(), list);
else
local method recur (lists :: <list>)
if (every?(pair?, lists))
concatenate(apply(func, head(lists)),
recur(tails(lists)));
else
#();
end if;
end method recur;
recur(pair(list, as(<list>, lists)));
end if;
end method concatenate-map;
// PAIR-DO -- similar to do, but FUNC is applied to successive
// sublists.
//
define function pair-do
(func :: <function>, lst :: <list>, #rest lists)
=> false :: <boolean>;
if (empty?(lists)) // fast path
local method recurse (lst)
if (pair?(lst))
let t = tail(lst); // grab the tail in case FUNC sets it
func(lst);
recurse(t);
end if;
end method;
recurse(lst);
else
local method recur (lst)
if (every?(pair?, lst))
let t = tails(lst);
apply(func, lst);
recur(t);
end if;
end method;
recur(pair(list, as(<list>, lists)));
end if;
#f;
end function pair-do;
// CHOOSE-MAP -- map FUNC across LST and save up all the results that
// satisfy PRED.
//
define method choose-map
(pred :: <function>, func :: <function>,
seq :: <sequence>, #rest seqs)
=> new-sequence :: <sequence>;
choose(pred, apply(map, func, seq, seqs));
end method choose-map;
// CHOOSE-MAP -- specialized version for lists.
//
define method choose-map
(pred :: <function>, func :: <function>,
lst :: <list>, #rest lists)
=> new-list :: <list>;
if (empty?(lists)) // fast path
foldr(method (elt, acc)
let res = func(elt);
if (pred(res)) pair(res, acc) else acc end if;
end method,
#(), lst);
else
local method recur (lst)
if (every?(pair, lst))
let t = tails(lst);
let res = apply(func, heads(lst));
if (pred(res))
pair(res, recur(tails(lst)));
else
recur(t);
end if;
else
#();
end if;
end method;
recur(pair(list, as(<list>, lists)));
end if;
end method choose-map;
// PARTITION -- partition a list in values that satisfy PRED and in
// values that don't.
//
// This is a rather lame implementation.
//
define function partition (pred :: <function>, seq :: <sequence>)
=> (winners :: <list>, losers :: <list>);
let (winners, losers) = values(#(), #());
for (elt in seq)
if (pred(elt))
winners := pair(elt, winners);
else
losers := pair(elt, losers);
end if;
end for;
values(reverse(winners), reverse(losers));
end function partition;
// ASSOC -- Find the tuple associated with KEY in the association
// sequence SEQ.
//
define function assoc (elt, seq :: <sequence>,
#key key = head, test = \=)
find(method (entry) test(elt, key(entry)) end, seq);
end function assoc;
// APAIR -- cons a new pair #(KEY, DATUM) on the head of ALIST.
//
define function apair (key, datum, aseq :: <sequence>,
#key cons = pair, add: add-fun = xpair)
=> new-aseq :: <sequence>;
add-fun(aseq, cons(key, datum));
end function apair;
// ALIST-COPY -- copy an "alist", actually any sequence that can act
// like an alist.
//
define function alist-copy (alist :: <sequence>,
#key key = head, datum = tail,
cons = pair)
=> new-alist :: <sequence>;
map(method (elt) cons(key(elt), datum(elt)) end, alist);
end function alist-copy;
// ALIST-DELETE -- delete all members keyed by ELT-KEY from ALIST.
//
define function alist-delete (elt, alist :: <sequence>,
#key key = head, test = \=)
choose(method (entry) ~test(elt, key(entry)) end,
alist);
end function alist-delete;
// SATISFIES -- find the first element that satisfies PRED.
//
define method satisfies
(pred :: <function>, seq :: <sequence>,
#key failure = #f)
=> index;
block (return)
for (entry keyed-by i in seq)
if (pred(entry)) return(i) end if;
end for;
failure;
end block;
end method satisfies;
// INDEX -- find the position of ELT is SEQ.
//
define method index (elt, seq :: <sequence>,
#key test = \=, failure = #f)
=> index;
satisfies(method (entry) test(elt, entry) end,
seq, failure: failure);
end method index;
// FIND -- returns the first true value produced by PRED.
//
define method find (pred :: <function>, seq :: <sequence>,
#key failure = #f)
let res = find-tail(pred, seq);
if (res) res[0] else #f end;
end method find;
// FIND-TAIL -- returns the subsequence after (and including) the
// first element that satisfies PRED.
//
define method find-tail (pred :: <function>, seq :: <sequence>,
#key failure = #f)
let index = satisfies(pred, seq);
if (index = 0)
seq;
elseif (index)
copy-sequence(seq, start: index);
else
failure;
end;
end method find-tail;
// FIND-TAIL -- specialized version for lists.
//
define method find-tail (pred :: <function>, lst :: <pair>,
#key failure = #f)
local method recur (elt, lst :: <list>)
if (pair?(lst))
if (pred(elt)) lst else recur(head(lst), tail(lst)) end;
else
failure;
end if;
end method recur;
recur(head(lst), tail(lst));
end method find-tail;
define method find-tail (pred :: <function>, lst :: <empty-list>,
#key failure = #f);
failure;
end method find-tail;
// PRECEDES? -- checks whether ELT-1 precedes ELT-2 in SEQ.
//
define method precedes?(elt-1, elt-2, seq :: <sequence>,
#key test = \=, not-found = #f)
=> precedes? :: <boolean>;
let elt-1-seen? = #f;
let elt-2-seen? = #f;
block (return)
for (elt in seq)
if (test(elt, elt-1))
if (elt-2-seen?)
return(#f);
else
elt-1-seen? := #t;
end if;
elseif (test(elt, elt-2))
if (elt-1-seen?)
return(#t);
else
elt-2-seen? := #t;
end if;
end if;
end for;
not-found;
end block;
end method precedes?;