~ chicken-core (chicken-5) /data-structures.scm


  1;;; data-structures.scm - Optional data structures extensions
  2;
  3; Copyright (c) 2008-2022, The CHICKEN Team
  4; All rights reserved.
  5;
  6; Redistribution and use in source and binary forms, with or without
  7; modification, are permitted provided that the following conditions
  8; are met:
  9;
 10;   Redistributions of source code must retain the above copyright notice, this list of conditions and the following
 11;     disclaimer. 
 12;   Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
 13;     disclaimer in the documentation and/or other materials provided with the distribution. 
 14;   Neither the name of the author nor the names of its contributors may be used to endorse or promote
 15;     products derived from this software without specific prior written permission. 
 16;
 17; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
 18; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 19; AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
 20; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 21; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 22; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 23; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 24; OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 25; POSSIBILITY OF SUCH DAMAGE.
 26
 27
 28(declare
 29 (unit data-structures))
 30
 31(module chicken.string
 32  (conc ->string string-chop string-chomp
 33   string-compare3 string-compare3-ci
 34   reverse-list->string reverse-string-append
 35   string-intersperse string-split
 36   string-translate string-translate*
 37   substring=? substring-ci=?
 38   substring-index substring-index-ci)
 39
 40(import scheme)
 41(import chicken.base)
 42(import chicken.condition)
 43(import chicken.fixnum)
 44(import chicken.foreign)
 45
 46; (reverse-string-append l) = (apply string-append (reverse l))
 47
 48(define (reverse-string-append l)
 49  (define (rev-string-append l i)
 50    (if (pair? l)
 51      (let* ((str (car l))
 52	     (len (string-length str))
 53	     (result (rev-string-append (cdr l) (fx+ i len))))
 54	(let loop ((j 0) (k (fx- (fx- (string-length result) i) len)))
 55	  (if (fx< j len)
 56	    (begin
 57	      (string-set! result k (string-ref str j))
 58	      (loop (fx+ j 1) (fx+ k 1)))
 59	    result)))
 60      (make-string i)))
 61  (rev-string-append l 0))
 62
 63(define (reverse-list->string l)
 64  (##sys#reverse-list->string l))
 65
 66;;; Anything->string conversion:
 67
 68(define ->string 
 69  (lambda (x)
 70    (cond [(string? x) x]
 71	  [(symbol? x) (symbol->string x)]
 72	  [(char? x) (string x)]
 73	  [(number? x) (##sys#number->string x)]
 74	  [else 
 75	   (let ([o (open-output-string)])
 76	     (display x o)
 77	     (get-output-string o) ) ] ) ) )
 78
 79(define conc
 80  (lambda args
 81    (apply string-append (map ->string args)) ) )
 82
 83
 84;;; Search one string inside another:
 85
 86(let ()
 87  (define (traverse which where start test loc)
 88    (##sys#check-string which loc)
 89    (##sys#check-string where loc)
 90    (let* ((wherelen (##sys#size where))
 91	   (whichlen (##sys#size which))
 92	   (end (fx- wherelen whichlen)))
 93      (##sys#check-fixnum start loc)
 94      (if (and (fx>= start 0)
 95	       (fx>= wherelen start))
 96	  (if (fx= whichlen 0)
 97	      start
 98	      (and (fx>= end 0)
 99		   (let loop ((istart start))
100		     (cond ((fx> istart end) #f)
101			   ((test istart whichlen) istart)
102			   (else (loop (fx+ istart 1)))))))
103	  (##sys#error-hook (foreign-value "C_OUT_OF_RANGE_ERROR" int)
104			    loc
105			    start
106			    wherelen))))
107
108  (set! ##sys#substring-index 
109    (lambda (which where start)
110      (traverse 
111       which where start
112       (lambda (i l) (##core#inline "C_substring_compare" which where 0 i l))
113       'substring-index) ) )
114  (set! ##sys#substring-index-ci 
115    (lambda (which where start)
116      (traverse
117       which where start
118       (lambda (i l) (##core#inline "C_substring_compare_case_insensitive" which where 0 i l)) 
119       'substring-index-ci) ) ) )
120
121(define (substring-index which where #!optional (start 0))
122  (##sys#substring-index which where start) )
123
124(define (substring-index-ci which where #!optional (start 0))
125  (##sys#substring-index-ci which where start) )
126
127
128;;; 3-Way string comparison:
129
130(define (string-compare3 s1 s2)
131  (##sys#check-string s1 'string-compare3)
132  (##sys#check-string s2 'string-compare3)
133  (let ((len1 (##sys#size s1))
134	(len2 (##sys#size s2)) )
135    (let* ((len-diff (fx- len1 len2)) 
136	   (cmp (##core#inline "C_string_compare" s1 s2 (if (fx< len-diff 0) len1 len2))))
137      (if (fx= cmp 0) 
138	  len-diff 
139	  cmp))))
140
141(define (string-compare3-ci s1 s2)
142  (##sys#check-string s1 'string-compare3-ci)
143  (##sys#check-string s2 'string-compare3-ci)
144  (let ((len1 (##sys#size s1))
145	(len2 (##sys#size s2)) )
146    (let* ((len-diff (fx- len1 len2)) 
147	   (cmp (##core#inline "C_string_compare_case_insensitive" s1 s2 (if (fx< len-diff 0) len1 len2))))
148      (if (fx= cmp 0) 
149	  len-diff 
150	  cmp))))
151
152
153;;; Substring comparison:
154
155(define (##sys#substring=? s1 s2 start1 start2 n)
156  (##sys#check-string s1 'substring=?)
157  (##sys#check-string s2 'substring=?)
158  (##sys#check-range start1 0 (fx+ (##sys#size s1) 1) 'substring=?)
159  (##sys#check-range start2 0 (fx+ (##sys#size s2) 1) 'substring=?)
160  (let* ((maxlen (fxmin (fx- (##sys#size s1) start1)
161                        (fx- (##sys#size s2) start2)))
162         (len (if n
163                  (begin (##sys#check-range n 0 (fx+ maxlen 1) 'substring=?) n)
164                  maxlen)))
165    (##core#inline "C_substring_compare" s1 s2 start1 start2 len) ) )
166
167(define (substring=? s1 s2 #!optional (start1 0) (start2 0) len)
168  (##sys#substring=? s1 s2 start1 start2 len) )
169
170(define (##sys#substring-ci=? s1 s2 start1 start2 n)
171  (##sys#check-string s1 'substring-ci=?)
172  (##sys#check-string s2 'substring-ci=?)
173  (##sys#check-range start1 0 (fx+ (##sys#size s1) 1) 'substring-ci=?)
174  (##sys#check-range start2 0 (fx+ (##sys#size s2) 1) 'substring-ci=?)
175  (let* ((maxlen (fxmin (fx- (##sys#size s1) start1)
176                        (fx- (##sys#size s2) start2)))
177         (len (if n
178                  (begin (##sys#check-range n 0 (fx+ maxlen 1) 'substring-ci=?) n)
179                  maxlen)))
180    (##core#inline "C_substring_compare_case_insensitive"
181		   s1 s2 start1 start2 len) ) )
182
183(define (substring-ci=? s1 s2 #!optional (start1 0) (start2 0) len)
184  (##sys#substring-ci=? s1 s2 start1 start2 len) )
185
186
187;;; Split string into substrings:
188
189(define string-split
190  (lambda (str . delstr-and-flag)
191    (##sys#check-string str 'string-split)
192    (let* ([del (if (null? delstr-and-flag) "\t\n " (car delstr-and-flag))]
193	   [flag (if (fx= (length delstr-and-flag) 2) (cadr delstr-and-flag) #f)]
194	   [strlen (##sys#size str)] )
195      (##sys#check-string del 'string-split)
196      (let ([dellen (##sys#size del)] 
197	    [first #f] )
198	(define (add from to last)
199	  (let ([node (cons (##sys#substring str from to) '())])
200	    (if first
201		(##sys#setslot last 1 node)
202		(set! first node) ) 
203	    node) )
204	(let loop ([i 0] [last #f] [from 0])
205	  (cond [(fx>= i strlen)
206		 (when (or (fx> i from) flag) (add from i last))
207		 (or first '()) ]
208		[else
209		 (let ([c (##core#inline "C_subchar" str i)])
210		   (let scan ([j 0])
211		     (cond [(fx>= j dellen) (loop (fx+ i 1) last from)]
212			   [(eq? c (##core#inline "C_subchar" del j))
213			    (let ([i2 (fx+ i 1)])
214			      (if (or (fx> i from) flag)
215				  (loop i2 (add from i last) i2)
216				  (loop i2 last i2) ) ) ]
217			   [else (scan (fx+ j 1))] ) ) ) ] ) ) ) ) ) )
218
219
220;;; Concatenate list of strings:
221
222(define (string-intersperse strs #!optional (ds " "))
223  (##sys#check-list strs 'string-intersperse)
224  (##sys#check-string ds 'string-intersperse)
225  (let ((dslen (##sys#size ds)))
226    (let loop1 ((ss strs) (n 0))
227      (cond ((##core#inline "C_eqp" ss '())
228	     (if (##core#inline "C_eqp" strs '())
229		 ""
230		 (let ((str2 (##sys#allocate-vector (fx- n dslen) #t #\space #f)))
231		   (let loop2 ((ss2 strs) (n2 0))
232		     (let* ((stri (##sys#slot ss2 0))
233			    (next (##sys#slot ss2 1)) 
234			    (strilen (##sys#size stri)) )
235		       (##core#inline "C_substring_copy" stri str2 0 strilen n2)
236		       (let ((n3 (fx+ n2 strilen)))
237			 (if (##core#inline "C_eqp" next '())
238			     str2
239			     (begin
240			       (##core#inline "C_substring_copy" ds str2 0 dslen n3)
241			       (loop2 next (fx+ n3 dslen)) ) ) ) ) ) ) ) )
242	    ((and (##core#inline "C_blockp" ss) (##core#inline "C_pairp" ss))
243	     (let ((stri (##sys#slot ss 0)))
244	       (##sys#check-string stri 'string-intersperse)
245	       (loop1 (##sys#slot ss 1)
246		      (fx+ (##sys#size stri) (fx+ dslen n)) ) ) )
247	    (else (##sys#error-not-a-proper-list strs)) ) ) ) )
248
249
250;;; Translate elements of a string:
251
252(define string-translate 
253  (lambda (str from . to)
254
255    (define (instring s)
256      (let ([len (##sys#size s)])
257	(lambda (c)
258	  (let loop ([i 0])
259	    (cond [(fx>= i len) #f]
260		  [(eq? c (##core#inline "C_subchar" s i)) i]
261		  [else (loop (fx+ i 1))] ) ) ) ) )
262
263    (let* ([from
264	    (cond [(char? from) (lambda (c) (eq? c from))]
265		  [(pair? from) (instring (list->string from))]
266		  [else
267		   (##sys#check-string from 'string-translate)
268		   (instring from) ] ) ]
269	   [to
270	    (and (pair? to)
271		 (let ([tx (##sys#slot to 0)])
272		   (cond [(char? tx) tx]
273			 [(pair? tx) (list->string tx)]
274			 [else
275			  (##sys#check-string tx 'string-translate)
276			  tx] ) ) ) ] 
277	   [tlen (and (string? to) (##sys#size to))] )
278      (##sys#check-string str 'string-translate)
279      (let* ([slen (##sys#size str)]
280	     [str2 (make-string slen)] )
281	(let loop ([i 0] [j 0])
282	  (if (fx>= i slen)
283	      (if (fx< j i)
284		  (##sys#substring str2 0 j)
285		  str2)
286	      (let* ([ci (##core#inline "C_subchar" str i)]
287		     [found (from ci)] )
288		(cond [(not found)
289		       (##core#inline "C_setsubchar" str2 j ci)
290		       (loop (fx+ i 1) (fx+ j 1)) ]
291		      [(not to) (loop (fx+ i 1) j)]
292		      [(char? to)
293		       (##core#inline "C_setsubchar" str2 j to)
294		       (loop (fx+ i 1) (fx+ j 1)) ]
295		      [(fx>= found tlen)
296		       (##sys#error 'string-translate "invalid translation destination" i to) ]
297		      [else 
298		       (##core#inline "C_setsubchar" str2 j (##core#inline "C_subchar" to found))
299		       (loop (fx+ i 1) (fx+ j 1)) ] ) ) ) ) ) ) ) )
300
301(define (string-translate* str smap)
302  (##sys#check-string str 'string-translate*)
303  (##sys#check-list smap 'string-translate*)
304  (let ((len (##sys#size str)))
305    (define (collect i from total fs)
306      (if (fx>= i len)
307	  (##sys#fragments->string
308	   total
309	   (##sys#fast-reverse 
310	    (if (fx> i from) 
311		(cons (##sys#substring str from i) fs)
312		fs) ) )
313	  (let loop ((smap smap))
314	    (if (null? smap) 
315		(collect (fx+ i 1) from (fx+ total 1) fs)
316		(let* ((p (car smap))
317		       (sm (car p))
318		       (smlen (string-length sm))
319		       (st (cdr p)) )
320		  (if (and (fx<= (fx+ i smlen) len)
321			   (##core#inline "C_substring_compare" str sm i 0 smlen))
322		      (let ((i2 (fx+ i smlen)))
323			(when (fx> i from)
324			  (set! fs (cons (##sys#substring str from i) fs)) )
325			(collect 
326			 i2 i2
327			 (fx+ total (string-length st))
328			 (cons st fs) ) ) 
329		      (loop (cdr smap)) ) ) ) ) ) )
330    (collect 0 0 0 '()) ) )
331
332
333;;; Chop string into substrings:
334
335(define (string-chop str len)
336  (##sys#check-string str 'string-chop)
337  (##sys#check-fixnum len 'string-chop)
338  (let ([total (##sys#size str)])
339    (let loop ([total total] [pos 0])
340      (cond [(fx<= total 0) '()]
341	    [(fx<= total len) (list (##sys#substring str pos (fx+ pos total)))]
342	    [else (cons (##sys#substring str pos (fx+ pos len)) (loop (fx- total len) (fx+ pos len)))] ) ) ) )
343	   
344
345;;; Remove suffix
346
347(define (string-chomp str #!optional (suffix "\n"))
348  (##sys#check-string str 'string-chomp)
349  (##sys#check-string suffix 'string-chomp)
350  (let* ((len (##sys#size str))
351	 (slen (##sys#size suffix)) 
352	 (diff (fx- len slen)) )
353    (if (and (fx>= len slen)
354	     (##core#inline "C_substring_compare" str suffix diff 0 slen) )
355	(##sys#substring str 0 diff)
356	str) ) )
357
358) ; chicken.string
359
360
361(module chicken.sort
362    (merge merge! sort sort! sorted? topological-sort)
363
364(import scheme chicken.base chicken.condition chicken.fixnum)
365
366;;; Defines: sorted?, merge, merge!, sort, sort!
367;;; Author : Richard A. O'Keefe (based on Prolog code by D.H.D.Warren)
368;;;
369;;; This code is in the public domain.
370
371;;; Updated: 11 June 1991
372;;; Modified for scheme library: Aubrey Jaffer 19 Sept. 1991
373;;; Updated: 19 June 1995
374
375;;; (sorted? sequence less?)
376;;; is true when sequence is a list (x0 x1 ... xm) or a vector #(x0 ... xm)
377;;; such that for all 1 <= i <= m,
378;;;	(not (less? (list-ref list i) (list-ref list (- i 1)))).
379
380; Modified by flw for use with CHICKEN:
381;
382
383
384(define (sorted? seq less?)
385    (cond
386	((null? seq)
387	    #t)
388	((vector? seq)
389	    (let ((n (vector-length seq)))
390		(if (<= n 1)
391		    #t
392		    (do ((i 1 (+ i 1)))
393			((or (= i n)
394			     (less? (vector-ref seq i)
395				    (vector-ref seq (- i 1))))
396			    (= i n)) )) ))
397	(else
398	    (let loop ((last (car seq)) (next (cdr seq)))
399		(or (null? next)
400		    (and (not (less? (car next) last))
401			 (loop (car next) (cdr next)) )) )) ))
402
403
404;;; (merge a b less?)
405;;; takes two lists a and b such that (sorted? a less?) and (sorted? b less?)
406;;; and returns a new list in which the elements of a and b have been stably
407;;; interleaved so that (sorted? (merge a b less?) less?).
408;;; Note:  this does _not_ accept vectors.  See below.
409
410(define (merge a b less?)
411    (cond
412	((null? a) b)
413	((null? b) a)
414	(else (let loop ((x (car a)) (a (cdr a)) (y (car b)) (b (cdr b)))
415	    ;; The loop handles the merging of non-empty lists.	 It has
416	    ;; been written this way to save testing and car/cdring.
417	    (if (less? y x)
418		(if (null? b)
419		    (cons y (cons x a))
420		    (cons y (loop x a (car b) (cdr b)) ))
421		;; x <= y
422		(if (null? a)
423		    (cons x (cons y b))
424		    (cons x (loop (car a) (cdr a) y b)) )) )) ))
425
426
427;;; (merge! a b less?)
428;;; takes two sorted lists a and b and smashes their cdr fields to form a
429;;; single sorted list including the elements of both.
430;;; Note:  this does _not_ accept vectors.
431
432(define (merge! a b less?)
433    (define (loop r a b)
434	(if (less? (car b) (car a))
435	    (begin
436		(set-cdr! r b)
437		(if (null? (cdr b))
438		    (set-cdr! b a)
439		    (loop b a (cdr b)) ))
440	    ;; (car a) <= (car b)
441	    (begin
442		(set-cdr! r a)
443		(if (null? (cdr a))
444		    (set-cdr! a b)
445		    (loop a (cdr a) b)) )) )
446    (cond
447	((null? a) b)
448	((null? b) a)
449	((less? (car b) (car a))
450	    (if (null? (cdr b))
451		(set-cdr! b a)
452		(loop b a (cdr b)))
453	    b)
454	(else ; (car a) <= (car b)
455	    (if (null? (cdr a))
456		(set-cdr! a b)
457		(loop a (cdr a) b))
458	    a)))
459
460
461;;; (sort! sequence less?)
462;;; sorts the list or vector sequence destructively.  It uses a version
463;;; of merge-sort invented, to the best of my knowledge, by David H. D.
464;;; Warren, and first used in the DEC-10 Prolog system.	 R. A. O'Keefe
465;;; adapted it to work destructively in Scheme.
466
467(define (sort! seq less?)
468    (define (step n)
469	(cond
470	    ((> n 2)
471		(let* ((j (quotient n 2))
472		       (a (step j))
473		       (k (- n j))
474		       (b (step k)))
475		    (merge! a b less?)))
476	    ((= n 2)
477		(let ((x (car seq))
478		      (y (cadr seq))
479		      (p seq))
480		    (set! seq (cddr seq))
481		    (if (less? y x) (begin
482			(set-car! p y)
483			(set-car! (cdr p) x)))
484		    (set-cdr! (cdr p) '())
485		    p))
486	    ((= n 1)
487		(let ((p seq))
488		    (set! seq (cdr seq))
489		    (set-cdr! p '())
490		    p))
491	    (else
492		'()) ))
493    (if (vector? seq)
494	(let ((n (vector-length seq))
495	      (vec seq))
496	  (set! seq (vector->list seq))
497	  (do ((p (step n) (cdr p))
498	       (i 0 (+ i 1)))
499	      ((null? p) vec)
500	    (vector-set! vec i (car p)) ))
501	;; otherwise, assume it is a list
502	(step (length seq)) ))
503
504;;; (sort sequence less?)
505;;; sorts a vector or list non-destructively.  It does this by sorting a
506;;; copy of the sequence.  My understanding is that the Standard says
507;;; that the result of append is always "newly allocated" except for
508;;; sharing structure with "the last argument", so (append x '()) ought
509;;; to be a standard way of copying a list x.
510
511(define (sort seq less?)
512    (if (vector? seq)
513	(list->vector (sort! (vector->list seq) less?))
514	(sort! (append seq '()) less?)))
515
516
517;;; Topological sort with cycle detection:
518;;
519;; A functional implementation of the algorithm described in Cormen,
520;; et al. (2009), Introduction to Algorithms (3rd ed.), pp. 612-615.
521
522(define (topological-sort dag pred)
523  (define (visit dag node edges path state)
524    (case (alist-ref node (car state) pred)
525      ((grey)
526       (abort
527        (##sys#make-structure
528         'condition
529         '(exn runtime cycle)
530         `((exn . message) "cycle detected"
531           (exn . arguments) ,(list (cons node (reverse path)))
532           (exn . call-chain) ,(get-call-chain)
533           (exn . location) topological-sort))))
534      ((black)
535       state)
536      (else
537       (let walk ((edges (or edges (alist-ref node dag pred '())))
538                  (state (cons (cons (cons node 'grey) (car state))
539                               (cdr state))))
540         (if (null? edges)
541             (cons (alist-update! node 'black (car state) pred)
542                   (cons node (cdr state)))
543             (let ((edge (car edges)))
544               (walk (cdr edges)
545                     (visit dag
546                            edge
547                            #f
548                            (cons edge path)
549                            state))))))))
550  (define normalized-dag
551    (foldl (lambda (result node)
552             (alist-update! (car node)
553                            (append (cdr node)
554                                    (or (alist-ref (car node) dag pred) '()))
555                            result
556                            pred))
557           '()
558           dag))
559  (let loop ((dag normalized-dag)
560             (state (cons (list) (list))))
561    (if (null? dag)
562        (cdr state)
563        (loop (cdr dag)
564              (visit dag
565                     (caar dag)
566                     (cdar dag)
567                     '()
568                     state)))))
569) ; chicken.sort
570
Trap