~ chicken-core (chicken-5) /data-structures.scm
Trap1;;; data-structures.scm - Optional data structures extensions2;3; Copyright (c) 2008-2022, The CHICKEN Team4; All rights reserved.5;6; Redistribution and use in source and binary forms, with or without7; modification, are permitted provided that the following conditions8; are met:9;10; Redistributions of source code must retain the above copyright notice, this list of conditions and the following11; disclaimer.12; Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following13; 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 promote15; 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 EXPRESS18; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY19; AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR20; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR21; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR22; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY23; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR24; OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE25; POSSIBILITY OF SUCH DAMAGE.262728(declare29 (unit data-structures))3031(module chicken.string32 (conc ->string string-chop string-chomp33 string-compare3 string-compare3-ci34 reverse-list->string reverse-string-append35 string-intersperse string-split36 string-translate string-translate*37 substring=? substring-ci=?38 substring-index substring-index-ci)3940(import scheme)41(import chicken.base)42(import chicken.condition)43(import chicken.fixnum)44(import chicken.foreign)4546; (reverse-string-append l) = (apply string-append (reverse l))4748(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 (begin57 (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))6263(define (reverse-list->string l)64 (##sys#reverse-list->string l))6566;;; Anything->string conversion:6768(define ->string69 (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 [else75 (let ([o (open-output-string)])76 (display x o)77 (get-output-string o) ) ] ) ) )7879(define conc80 (lambda args81 (apply string-append (map ->string args)) ) )828384;;; Search one string inside another:8586(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 start98 (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 loc105 start106 wherelen))))107108 (set! ##sys#substring-index109 (lambda (which where start)110 (traverse111 which where start112 (lambda (i l) (##core#inline "C_substring_compare" which where 0 i l))113 'substring-index) ) )114 (set! ##sys#substring-index-ci115 (lambda (which where start)116 (traverse117 which where start118 (lambda (i l) (##core#inline "C_substring_compare_case_insensitive" which where 0 i l))119 'substring-index-ci) ) ) )120121(define (substring-index which where #!optional (start 0))122 (##sys#substring-index which where start) )123124(define (substring-index-ci which where #!optional (start 0))125 (##sys#substring-index-ci which where start) )126127128;;; 3-Way string comparison:129130(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-diff139 cmp))))140141(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-diff150 cmp))))151152153;;; Substring comparison:154155(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 n163 (begin (##sys#check-range n 0 (fx+ maxlen 1) 'substring=?) n)164 maxlen)))165 (##core#inline "C_substring_compare" s1 s2 start1 start2 len) ) )166167(define (substring=? s1 s2 #!optional (start1 0) (start2 0) len)168 (##sys#substring=? s1 s2 start1 start2 len) )169170(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 n178 (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) ) )182183(define (substring-ci=? s1 s2 #!optional (start1 0) (start2 0) len)184 (##sys#substring-ci=? s1 s2 start1 start2 len) )185186187;;; Split string into substrings:188189(define string-split190 (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 first201 (##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 [else209 (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))] ) ) ) ] ) ) ) ) ) )218219220;;; Concatenate list of strings:221222(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 str2239 (begin240 (##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)) ) ) ) )248249250;;; Translate elements of a string:251252(define string-translate253 (lambda (str from . to)254255 (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))] ) ) ) ) )262263 (let* ([from264 (cond [(char? from) (lambda (c) (eq? c from))]265 [(pair? from) (instring (list->string from))]266 [else267 (##sys#check-string from 'string-translate)268 (instring from) ] ) ]269 [to270 (and (pair? to)271 (let ([tx (##sys#slot to 0)])272 (cond [(char? tx) tx]273 [(pair? tx) (list->string tx)]274 [else275 (##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 [else298 (##core#inline "C_setsubchar" str2 j (##core#inline "C_subchar" to found))299 (loop (fx+ i 1) (fx+ j 1)) ] ) ) ) ) ) ) ) )300301(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->string308 total309 (##sys#fast-reverse310 (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 (collect326 i2 i2327 (fx+ total (string-length st))328 (cons st fs) ) )329 (loop (cdr smap)) ) ) ) ) ) )330 (collect 0 0 0 '()) ) )331332333;;; Chop string into substrings:334335(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)))] ) ) ) )343344345;;; Remove suffix346347(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) ) )357358) ; chicken.string359360361(module chicken.sort362 (merge merge! sort sort! sorted? topological-sort)363364(import scheme chicken.base chicken.condition chicken.fixnum)365366;;; 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.370371;;; Updated: 11 June 1991372;;; Modified for scheme library: Aubrey Jaffer 19 Sept. 1991373;;; Updated: 19 June 1995374375;;; (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)))).379380; Modified by flw for use with CHICKEN:381;382383384(define (sorted? seq less?)385 (cond386 ((null? seq)387 #t)388 ((vector? seq)389 (let ((n (vector-length seq)))390 (if (<= n 1)391 #t392 (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 (else398 (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)) )) )) ))402403404;;; (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 stably407;;; interleaved so that (sorted? (merge a b less?) less?).408;;; Note: this does _not_ accept vectors. See below.409410(define (merge a b less?)411 (cond412 ((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 has416 ;; 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 <= y422 (if (null? a)423 (cons x (cons y b))424 (cons x (loop (car a) (cdr a) y b)) )) )) ))425426427;;; (merge! a b less?)428;;; takes two sorted lists a and b and smashes their cdr fields to form a429;;; single sorted list including the elements of both.430;;; Note: this does _not_ accept vectors.431432(define (merge! a b less?)433 (define (loop r a b)434 (if (less? (car b) (car a))435 (begin436 (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 (begin442 (set-cdr! r a)443 (if (null? (cdr a))444 (set-cdr! a b)445 (loop a (cdr a) b)) )) )446 (cond447 ((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)))459460461;;; (sort! sequence less?)462;;; sorts the list or vector sequence destructively. It uses a version463;;; 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'Keefe465;;; adapted it to work destructively in Scheme.466467(define (sort! seq less?)468 (define (step n)469 (cond470 ((> 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) (begin482 (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 (else492 '()) ))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 list502 (step (length seq)) ))503504;;; (sort sequence less?)505;;; sorts a vector or list non-destructively. It does this by sorting a506;;; copy of the sequence. My understanding is that the Standard says507;;; that the result of append is always "newly allocated" except for508;;; sharing structure with "the last argument", so (append x '()) ought509;;; to be a standard way of copying a list x.510511(define (sort seq less?)512 (if (vector? seq)513 (list->vector (sort! (vector->list seq) less?))514 (sort! (append seq '()) less?)))515516517;;; 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.521522(define (topological-sort dag pred)523 (define (visit dag node edges path state)524 (case (alist-ref node (car state) pred)525 ((grey)526 (abort527 (##sys#make-structure528 'condition529 '(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 (else537 (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 dag546 edge547 #f548 (cons edge path)549 state))))))))550 (define normalized-dag551 (foldl (lambda (result node)552 (alist-update! (car node)553 (append (cdr node)554 (or (alist-ref (car node) dag pred) '()))555 result556 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 dag565 (caar dag)566 (cdar dag)567 '()568 state)))))569) ; chicken.sort570