Welcome to the CHICKEN Scheme pasting service
natural sort added by wasamasa on Fri Dec 23 12:09:55 2016
(use irregex data-structures test) ;; TODO: offer trimming/ci comparison? that's what the ruby ;; implementation does, but it strikes me as wasteful... (define (tokenize string) (reverse (irregex-fold '(or (=> number (+ num)) (=> string (+ (~ num)))) (lambda (_i m acc) (let ((number (irregex-match-substring m 'number)) (string (irregex-match-substring m 'string))) (if number (cons (string->number number) acc) (cons string acc)))) '() string))) (test '() (tokenize "")) (test '(123) (tokenize "123")) (test '("foo") (tokenize "foo")) (test '("foo" 123 "bar") (tokenize "foo123bar")) (define (clamp lower x upper) (min (max lower x) upper)) (test 0 (clamp -1 0 1)) (test -1 (clamp -1 -1 1)) (test -1 (clamp -1 -10 1)) (test 1 (clamp -1 1 1)) (test 1 (clamp -1 10 1)) (define (natural-string-compare string1 string2) (define (inner as bs) (cond ((and (null? as) (null? bs)) 0) ((null? as) -1) ((null? bs) 1) (else (let ((a (car as)) (b (car bs))) (if (and (number? a) (number? b)) (cond ((< a b) -1) ((> a b) 1) (else (inner (cdr as) (cdr bs)))) (let* ((a (if (number? a) (number->string a) a)) (b (if (number? b) (number->string b) b)) (result (string-compare3 a b))) (if (zero? result) (inner (cdr as) (cdr bs)) ;; unlike what the docs suggest, string-compare3 ;; does *not* clamp to -1 and +1 (clamp -1 result 1)))))))) (inner (tokenize string1) (tokenize string2))) (test 0 (natural-string-compare "" "")) (test -1 (natural-string-compare "" "foo")) (test +1 (natural-string-compare "foo" "")) (test 0 (natural-string-compare "foo" "foo")) (test -1 (natural-string-compare "bar" "foo")) (test +1 (natural-string-compare "foo" "bar")) (test 0 (natural-string-compare "foo123" "foo123")) (test -1 (natural-string-compare "foo" "foo123")) (test +1 (natural-string-compare "foo123" "foo")) (test -1 (natural-string-compare "foo2" "foo123")) (test -1 (natural-string-compare "foo99" "foo123")) (test 0 (natural-string-compare "foo123bar" "foo123bar")) (test -1 (natural-string-compare "foo123bar" "foo123baz")) (test +1 (natural-string-compare "foo123qux" "foo123baz")) (define (natural-string<? string1 string2) (if (< (natural-string-compare string1 string2) 0) #t #f)) (define (natural-string<=? string1 string2) (if (<= (natural-string-compare string1 string2) 0) #t #f)) (define (natural-string=? string1 string2) (if (zero? (natural-string-compare string1 string2)) #t #f)) (define (natural-string<>? string1 string2) (if (not (zero? (natural-string-compare string1 string2))) #t #f)) (define (natural-string>? string1 string2) (if (> (natural-string-compare string1 string2) 0) #t #f)) (define (natural-string>=? string1 string2) (if (>= (natural-string-compare string1 string2) 0) #t #f)) (define (natural-sort strings) (sort strings natural-string<?)) (test '("1" "2" "3" "4" "10" "20" "30" "100") (natural-sort '("1" "10" "100" "2" "20" "3" "30" "4"))) (test '("a" "a0" "a1" "a1a" "a1b" "a2" "a10" "a20") (natural-sort '("a10" "a" "a20" "a1b" "a1a" "a2" "a0" "a1")))