(module list mzscheme
(require (lib "contract.ss")
(lib "etc.ss")
(only (lib "list.ss" "srfi" "1") make-list fold take drop alist-delete)
(lib "cut.ss" "srfi" "26")
(file "base.ss")
(file "debug.ss"))
(define quick-list/c
(or/c pair? null?))
(define tree/c any/c)
(provide alist-accessor
alist-accessor/default
alist-mutator
alist-mutator/append)
(provide/contract
[mutable-cons (-> any/c any/c any)]
[list-swap (-> quick-list/c any/c any/c any)]
[list-delimit (-> quick-list/c any/c any)]
[merge-sorted-lists (-> quick-list/c quick-list/c procedure? procedure? any)]
[char-iota (opt-> (integer?) (char?) any)]
[list-pad (opt-> (quick-list/c integer?) (any/c) any)]
[list-pad-right (opt-> (quick-list/c integer?) (any/c) any)]
[tree-map (-> procedure? tree/c any)]
[tree-for-each (-> procedure? tree/c any)]
[assoc-value (-> any/c quick-list/c any)]
[assoc-value/default (-> any/c quick-list/c any/c any)]
[alist-set (-> any/c any/c quick-list/c any)]
[alist-map (-> procedure? quick-list/c any)]
[alist-for-each (-> procedure? quick-list/c any)]
[alist-merge (opt-> (quick-list/c quick-list/c) ((symbols 'first 'second)) any)])
(provide alist-delete)
(define mutable-cons cons)
(define (list-swap data index1 index2)
(cond [(< index2 index1)
(list-swap data index2 index1)]
[(= index1 index2)
(raise-exn exn:fail:contract
(format "List indices must be differnet: ~a ~a" index1 index2))]
[(or (< index2 0) (> index1 (length data)))
(raise-exn exn:fail:contract
(format "List indices out of bounds: ~a ~a" index1 index2))]
[else
(let ([item1 (list-ref data index1)]
[item2 (list-ref data index2)]
[slice0-1 (take data index1)]
[slice1-2 (take (drop data (add1 index1)) (sub1 (- index2 index1)))]
[slice2-3 (drop data (add1 index2))])
(append slice0-1
(cons item2 slice1-2)
(cons item1 slice2-3)))]))
(define (list-delimit list delimiter)
(if (null? list)
null
(let loop ([rest list])
(if (null? (cdr rest))
(cons (car rest)
null)
(cons (car rest)
(cons delimiter
(loop (cdr rest))))))))
(define (merge-sorted-lists list1 list2 same? less-than?)
(define (swallow item list)
(cond [(null? list) list]
[(same? item (car list)) (swallow item (cdr list))]
[else list]))
(define (merge list1 list2)
(cond [(null? list1) list2]
[(null? list2) list1]
[else (let ([head1 (car list1)]
[head2 (car list2)])
(cond [(same? head1 head2)
(cons head1 (merge (swallow head1 list1)
(swallow head2 list2)))]
[(less-than? head1 head2)
(cons head1 (merge (swallow head1 list1) list2))]
[else
(cons head2 (merge list1 (swallow head2 list2)))]))]))
(merge list1 list2))
(define char-iota
(opt-lambda (count [start #\a])
(let loop ([i 0] [curr (char->integer start)])
(if (< i count)
(cons (integer->char curr)
(loop (add1 i) (add1 curr)))
null))))
(define list-pad
(opt-lambda (lis target-length [item #f])
(let loop ([current-length (length lis)] [accum lis])
(if (< current-length target-length)
(loop (add1 current-length) (cons item accum))
accum))))
(define list-pad-right
(opt-lambda (lis target-length [item #f])
(reverse (list-pad (reverse lis) target-length item))))
(define (tree-map fn tree)
(let loop ([item tree])
(cond
[(list? item) (map loop item)]
[(pair? item) (cons (loop (car item)) (loop (cdr item)))]
[else (fn item)])))
(define (tree-for-each fn tree)
(let loop ([item tree])
(cond
[(list? item)
(for-each loop item)]
[(pair? item)
(loop (car item))
(loop (cdr item))]
[else
(fn item)])))
(define (assoc-value key alist)
(let ([kvp (assoc key alist)])
(if kvp
(cdr kvp)
(raise-exn exn:fail:unlib
(format "Key ~a not found in ~a.~n" key alist)))))
(define (assoc-value/default key alist default)
(let ([kvp (assoc key alist)])
(if kvp
(cdr kvp)
default)))
(define-syntax (alist-accessor stx)
(syntax-case stx ()
[(_ alist)
#'(lambda (key)
(assoc-value key alist))]))
(define-syntax (alist-accessor/default stx)
(syntax-case stx ()
[(_ alist default)
#'(lambda (key)
(assoc-value/default key alist default))]))
(define (alist-set key value alist)
(let* ([found #f]
[new-alist
(map
(lambda (kvp)
(if (equal? key (car kvp))
(begin
(set! found #t)
(cons (car kvp) value))
kvp))
alist)])
(if found
new-alist
(append new-alist
(list (cons key value))))))
(define-syntax (alist-mutator stx)
(syntax-case stx ()
[(_ alist)
#'(lambda (key val)
(set! alist (alist-set key val alist)))]))
(define-syntax (alist-mutator/append stx)
(syntax-case stx ()
[(_ alist)
#'(lambda (key val)
(let ([curr (assoc-value/default key alist null)])
(set! alist (alist-set key (append curr (list val)) alist))))]))
(define (alist-map proc alist)
(map
(lambda (kvp)
(if (pair? kvp)
(proc (car kvp) (cdr kvp))
(raise-exn
exn:fail:unlib
(format "alist-map: expected a pair: ~a" kvp))))
alist))
(define (alist-for-each proc alist)
(for-each
(lambda (kvp)
(if (pair? kvp)
(proc (car kvp) (cdr kvp))
(raise-exn
exn:fail:unlib
(format "alist-for-each: expected a pair: ~a" kvp))))
alist))
(define alist-merge
(opt-lambda (list1 list2 [prefer 'first])
(define proc
(if (eq? prefer 'first)
(lambda (item accum)
(let ([key (car item)]
[x (car accum)]
[y (cdr accum)])
(if (assoc key x)
(cons x y)
(cons x (cons item y)))))
(lambda (item accum)
(let ([key (car item)]
[x (car accum)]
[y (cdr accum)])
(if (assoc key x)
(cons (alist-set key (cdr item) x) y)
(cons x (cons item y)))))))
(let ([x-and-y (fold proc (cons list1 null) list2)])
(append (car x-and-y) (reverse (cdr x-and-y))))))
)