added a powerful remove-duplicates and tests
svn: r9373
This commit is contained in:
parent
352036ea42
commit
9b0fc20d3e
|
@ -14,7 +14,8 @@
|
||||||
|
|
||||||
append*
|
append*
|
||||||
flatten
|
flatten
|
||||||
add-between)
|
add-between
|
||||||
|
remove-duplicates)
|
||||||
|
|
||||||
(define (first x)
|
(define (first x)
|
||||||
(if (and (pair? x) (list? x))
|
(if (and (pair? x) (list? x))
|
||||||
|
@ -107,3 +108,72 @@
|
||||||
;; (if (null? ls)
|
;; (if (null? ls)
|
||||||
;; ls
|
;; ls
|
||||||
;; (append l (car ls) (loop (cdr ls))))))]))
|
;; (append l (car ls) (loop (cdr ls))))))]))
|
||||||
|
|
||||||
|
(define (remove-duplicates l
|
||||||
|
#:test [=? equal?]
|
||||||
|
#:mode [mode 'naive]
|
||||||
|
#:keep [keep 'first]
|
||||||
|
#:ordered? [ordered? #t])
|
||||||
|
(unless (list? l) (raise-type-error 'remove-duplicates "list" l))
|
||||||
|
(unless (memq keep '(first last))
|
||||||
|
(raise-type-error 'remove-duplicates "'first or 'last" keep))
|
||||||
|
(case mode
|
||||||
|
;; plain n^2 list traversal (optimized, since it's a common case)
|
||||||
|
[(naive)
|
||||||
|
(if (eq? 'first keep)
|
||||||
|
(let-syntax ([loop (syntax-rules ()
|
||||||
|
[(_ search)
|
||||||
|
(let loop ([l l] [seen null])
|
||||||
|
(if (null? l)
|
||||||
|
l
|
||||||
|
(let ([x (car l)] [l (cdr l)])
|
||||||
|
(if (search x seen)
|
||||||
|
(loop l seen)
|
||||||
|
(cons x (loop l (cons x seen)))))))])])
|
||||||
|
(cond [(eq? =? equal?) (loop member)]
|
||||||
|
[(eq? =? eq?) (loop memq)]
|
||||||
|
[else (loop (lambda (x seen)
|
||||||
|
(ormap (lambda (y) (=? x y)) seen)))]))
|
||||||
|
(let-syntax ([loop (syntax-rules ()
|
||||||
|
[(_ search)
|
||||||
|
(let loop ([l l])
|
||||||
|
(if (null? l)
|
||||||
|
l
|
||||||
|
(let ([x (car l)] [l (cdr l)])
|
||||||
|
(if (search x l)
|
||||||
|
(loop l)
|
||||||
|
(cons x (loop l))))))])])
|
||||||
|
(cond [(eq? =? equal?) (loop member)]
|
||||||
|
[(eq? =? eq?) (loop memq)]
|
||||||
|
[else (loop (lambda (x seen)
|
||||||
|
(ormap (lambda (y) (=? x y)) seen)))])))]
|
||||||
|
[(hash)
|
||||||
|
(let ([h (make-hash)])
|
||||||
|
(if ordered?
|
||||||
|
(begin (for ([x l]) (hash-set! h x (add1 (hash-ref h x 0))))
|
||||||
|
(filter (if (eq? 'first keep)
|
||||||
|
(lambda (x) (begin0 (hash-ref h x) (hash-set! h x #f)))
|
||||||
|
(lambda (x)
|
||||||
|
(let ([c (sub1 (hash-ref h x))])
|
||||||
|
(hash-set! h x c)
|
||||||
|
(eq? 0 c))))
|
||||||
|
l))
|
||||||
|
;; note: the hash entries always have the first occurrence as the key
|
||||||
|
;; and the last one as the value
|
||||||
|
(begin (for ([x l]) (hash-set! h x x))
|
||||||
|
(hash-map h (if (eq? 'first keep)
|
||||||
|
(lambda (x y) x) (lambda (x y) y))))))]
|
||||||
|
[(sorted)
|
||||||
|
(if (null? l)
|
||||||
|
l
|
||||||
|
(if (eq? 'last keep)
|
||||||
|
(let loop ([l l])
|
||||||
|
(let ([x (car l)] [r (cdr l)])
|
||||||
|
(cond [(null? r) l]
|
||||||
|
[(=? x (car r)) (loop r)]
|
||||||
|
[else (cons x (loop r))])))
|
||||||
|
(let loop ([x (car l)] [l (cdr l)])
|
||||||
|
(cond [(null? l) (list x)]
|
||||||
|
[(=? x (car l)) (loop x (cdr l))]
|
||||||
|
[else (cons x (loop (car l) (cdr l)))]))))]
|
||||||
|
[else (error 'remove-duplicates "unknown mode: ~e" mode)]))
|
||||||
|
|
|
@ -200,4 +200,36 @@
|
||||||
(test '(9 1 8 1 7) add-between '(9 8 7) 1)
|
(test '(9 1 8 1 7) add-between '(9 8 7) 1)
|
||||||
(test '(9 (1) 8) add-between '(9 8) '(1)))
|
(test '(9 (1) 8) add-between '(9 8) '(1)))
|
||||||
|
|
||||||
|
;; ---------- remove-duplicates ----------
|
||||||
|
(let ()
|
||||||
|
(define rd remove-duplicates)
|
||||||
|
;; basic 'naive tests
|
||||||
|
(test '() rd '())
|
||||||
|
(test '(a) rd '(a a a a))
|
||||||
|
(test '(a b) rd '(a b))
|
||||||
|
(test '(a b) rd '(a b a b a b))
|
||||||
|
(test '(a b) rd '(a a a b b b))
|
||||||
|
(test '(a b) rd '(a b b a) #:keep 'first)
|
||||||
|
(test '(b a) rd '(a b b a) #:keep 'last)
|
||||||
|
;; test with 'hash
|
||||||
|
(test '() rd '() #:mode 'hash)
|
||||||
|
(test '(a) rd '(a a a a) #:mode 'hash)
|
||||||
|
(test '(a b) rd '(a b) #:mode 'hash)
|
||||||
|
(test '(a b) rd '(a b a b a b) #:mode 'hash #:ordered? #t)
|
||||||
|
(test '(a b) rd '(a a a b b b) #:mode 'hash #:ordered? #t)
|
||||||
|
(test '(a b) rd '(a b b a) #:keep 'first #:mode 'hash #:ordered? #t)
|
||||||
|
(test '(b a) rd '(a b b a) #:keep 'last #:mode 'hash #:ordered? #t)
|
||||||
|
;; test with 'sorted
|
||||||
|
(test '() rd '() #:mode 'sorted)
|
||||||
|
(test '(a) rd '(a a a a) #:mode 'sorted)
|
||||||
|
(test '(a b) rd '(a b) #:mode 'sorted)
|
||||||
|
(test '(a b a b a b) rd '(a b a b a b) #:mode 'sorted)
|
||||||
|
(test '(a b) rd '(a a a b b b) #:mode 'sorted)
|
||||||
|
(let ([a1 "a"] [a2 "a"] [b1 "b"] [b2 "b"])
|
||||||
|
(test #t andmap eq? (list a1 b1)
|
||||||
|
(rd (list a1 a2 b1 b2) #:mode 'sorted #:keep 'first))
|
||||||
|
(test #t andmap eq? (list a2 b2)
|
||||||
|
(rd (list a1 a2 b1 b2) #:mode 'sorted #:keep 'last)))
|
||||||
|
)
|
||||||
|
|
||||||
(report-errs)
|
(report-errs)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user