separate `shift` into `shift` and `shifts`

pull/3/head
Matthew Butterick 9 years ago
parent 3d6b75699a
commit fbfa56f2eb

@ -26,15 +26,15 @@
values->list
[sublist (list? index? index? . -> . list?)]
[break-at (list? (and/c coerce/list? (or/c empty? increasing-nonnegative-list?)) . -> . list-of-lists?)]
[shift ((list? (or/c integer? integers?)) (any/c boolean?) . ->* . list?)]
[shift/values ((list? (or/c integer? integers?)) (any/c) . ->* . any)])
[shift ((list? integer?) (any/c boolean?) . ->* . list?)]
[shifts ((list? integers?) (any/c boolean?) . ->* . (listof list?))]
[shift/values ((list? (or/c integers? integer?)) (any/c boolean?) . ->* . any)])
;; todo: can this work in typed context? couldn't figure out how to polymorphically `apply values`
;; macro doesn't work either
(define (shift/values xs shift-amount-or-amounts [fill-item #f])
(apply (if (list? shift-amount-or-amounts)
values
(λ xs xs))
(shift xs shift-amount-or-amounts fill-item)))
(define (shift/values xs shift-amount-or-amounts [fill-item #f] [cycle #f])
(apply values ((if (list? shift-amount-or-amounts)
shifts
shift) xs shift-amount-or-amounts fill-item cycle)))

@ -188,31 +188,52 @@ Break @racket[_lst] into smaller lists at the index positions in @racket[_indexe
@defproc[
(shift
[lst list?]
[how-far (or/c integer? (listof integer?))]
[fill-item any/c #f])
(or/c list? (listof list?))]
Move the items in @racket[_lst] to the right (if @racket[_how-far] is positive) or left (if negative). Vacated spaces in the list are filled with @racket[_fill-item], so the result list is always the same length as the input list. (If you don't care about the lengths being the same, you probably want @racket[take] or @racket[drop] instead.) If @racket[_how-far] is a list rather than a single value, return a list of lists shifted by the designated amounts. If @racket[_how-far] is 0, return the original list. If @racket[_how-far] is bigger than the length of @racket[_lst], raise an error.
[how-far integer?]
[fill-item any/c #f]
[cycle? boolean? #f])
list?]
Move the items in @racket[_lst] to the right (if @racket[_how-far] is positive) or left (if @racket[_how-far] is negative). By default, vacated spaces in the list are filled with @racket[_fill-item]. But if @racket[_cycle?] is true, elements of the list wrap around (and @racket[_fill-item] is ignored). Either way, the result list is always the same length as the input list. (If you don't care about the lengths being the same, you probably want @racket[take] or @racket[drop] instead.) If @racket[_how-far] is 0, return the original list. If @racket[_how-far] is bigger than the length of @racket[_lst], raise an error.
@examples[#:eval my-eval
(define xs (range 5))
(shift xs 2)
(shift xs -2 0)
(shift xs '(-1 0 1) 'boing)
(shift xs 2 'boing)
(shift xs 2 'boing #t)
(shift xs 0)
(shift xs 42)
]
@defproc[
(shifts
[lst list?]
[how-far (listof integer?)]
[fill-item any/c #f]
[cycle? boolean? #f])
(listof list?)]
Same as @racket[shift], but @racket[_how-far] is a list of integers rather than a single integer, and the result is a list of lists rather than a single list.
@examples[#:eval my-eval
(define xs (range 5))
(shifts xs '(-2 2))
(shifts xs '(-2 2) 0)
(shifts xs '(-2 2) 'boing)
(shifts xs '(-2 2) 'boing #t)
]
@defproc[
(shift/values
[lst list?]
[how-far (or/c integer? (listof integer?))]
[fill-item any/c #f])
any]
@bold{Untyped only.} Same as @racket[shift], except that when @racket[_how-far] is a list, the resulting lists are returned as multiple values rather than as a list of lists.
@bold{Untyped only.} When @racket[_how-far] is a single integer, same as @racket[shift], but the resulting list is returned as values. When @racket[_how-far] is a list of integers, same as @racket[shifts], but the resulting lists are returned as multiple values rather than as a list of lists.
@examples[#:eval my-eval
(define xs (range 5))
(shift xs '(-1 0 1))
(shift xs 1)
(shift/values xs 1)
(shifts xs '(-1 0 1))
(shift/values xs '(-1 0 1))
]

@ -166,11 +166,15 @@
(define xs (range 5))
(check-equal? (map (λ(a b c) (list a b c)) (shift xs -1) (shift xs 0) (shift xs 1)) '((1 0 #f) (2 1 0) (3 2 1) (4 3 2) (#f 4 3)))
(check-equal? (shift xs '(-1 0 1) 'boing) `((1 2 3 4 boing) ,xs (boing 0 1 2 3)))
(check-equal? (shift xs 5 0) (make-list 5 0))
(check-equal? (map (λ(a b c) (list a b c)) (shift xs -1 'ignored #t) (shift xs 0 'ignored #t) (shift xs 1 'ignored #t)) '((1 0 4) (2 1 0) (3 2 1) (4 3 2) (0 4 3)))
(check-equal? (shifts xs '(-1 0 1) 'boing) `((1 2 3 4 boing) ,xs (boing 0 1 2 3)))
(check-equal? (shifts xs '(-1 0 1) 'boing #t) `((1 2 3 4 0) ,xs (4 0 1 2 3)))
(check-equal? (shift xs 5 0) (make-list 5 0))
(check-exn exn:fail? (λ() (shift xs -10))))
(eval-as-untyped
(check-equal? (filter-split '("foo" " " "bar" "\n" "\n" "ino") (λ(x) (< (string-length x) 3))) '(("foo")("bar")("ino")))
@ -215,6 +219,7 @@
(define xs (range 5))
(define ys (range 5))
(check-equal? (values->list (shift/values ys -1 'boing)) '(1 2 3 4 boing))
(check-equal? (values->list (shift/values ys '(-1 0 1) 'boing)) `((1 2 3 4 boing) ,xs (boing 0 1 2 3)))
(require xml)

@ -166,25 +166,39 @@
(define/typed+provide shift
(All (A) (case-> ((Listof (Option A)) (U Integer (Listof Integer)) -> (U (Listof (Option A)) (Listof (Listof (Option A)))))
((Listof (Option A)) (U Integer (Listof Integer)) (Option A) -> (U (Listof (Option A)) (Listof (Listof (Option A)))))
((Listof (Option A)) (U Integer (Listof Integer)) (Option A) Boolean -> (U (Listof (Option A)) (Listof (Listof (Option A)))))))
(All (A) (case-> ((Listof (Option A)) Integer -> (Listof (Option A)))
((Listof (Option A)) Integer (Option A) -> (Listof (Option A)))
((Listof (Option A)) Integer (Option A) Boolean -> (Listof (Option A)))))
(case-lambda
[(xs shift-amount-or-amounts)
(shift xs shift-amount-or-amounts #f #f)]
[(xs shift-amount-or-amounts fill-item)
(shift xs shift-amount-or-amounts fill-item #f)]
[(xs shift-amount-or-amounts fill-item cycle)
(define/typed (do-shift xs fill-item how-far)
(All (A2) ((Listof (Option A2)) (Option A2) Integer -> (Listof (Option A2))))
(define abs-how-far (abs how-far))
(cond
[(> abs-how-far (length xs)) (error 'shift "index is too large for list\nindex: ~a\nlist: ~v" how-far xs)]
[(= how-far 0) xs]
[(positive? how-far)
((inst append (Option A2)) ((inst make-list (Option A2)) abs-how-far fill-item) (drop-right xs abs-how-far))]
;; otherwise how-far is negative
[else (append (drop xs abs-how-far) (make-list abs-how-far fill-item))]))
(if (list? shift-amount-or-amounts)
((inst map (Listof (Option A)) Integer) (λ:([amount : Integer]) (do-shift xs fill-item amount)) shift-amount-or-amounts)
(do-shift xs fill-item shift-amount-or-amounts))]))
[(xs how-far)
(shift xs how-far #f #f)]
[(xs how-far fill-item)
(shift xs how-far fill-item #f)]
[(xs how-far fill-item cycle)
(define abs-how-far (abs how-far))
(cond
[(> abs-how-far (length xs)) (error 'shift "index is too large for list\nindex: ~a\nlist: ~v" how-far xs)]
[(= how-far 0) xs]
[(positive? how-far)
(define filler (if cycle
(take-right xs abs-how-far)
(make-list abs-how-far fill-item)))
(append filler (drop-right xs abs-how-far))]
[else ; how-far is negative
(define filler (if cycle
(take xs abs-how-far)
(make-list abs-how-far fill-item)))
(append (drop xs abs-how-far) filler)])]))
(define/typed+provide shifts
(All (A) (case-> ((Listof (Option A)) (Listof Integer) -> (Listof (Listof (Option A))))
((Listof (Option A)) (Listof Integer) (Option A) -> (Listof (Listof (Option A))))
((Listof (Option A)) (Listof Integer) (Option A) Boolean -> (Listof (Listof (Option A))))))
(case-lambda
[(xs how-fars)
(shifts xs how-fars #f #f)]
[(xs how-fars fill-item)
(shifts xs how-fars fill-item #f)]
[(xs how-fars fill-item cycle)
(map (λ:([how-far : Integer]) (shift xs how-far fill-item cycle)) how-fars)]))

Loading…
Cancel
Save