From fbfa56f2eb986bf1dcbad5ffb566094e65ecf969 Mon Sep 17 00:00:00 2001 From: Matthew Butterick Date: Wed, 13 May 2015 14:57:58 -0700 Subject: [PATCH] separate `shift` into `shift` and `shifts` --- sugar/list.rkt | 14 ++++----- sugar/scribblings/list.scrbl | 35 +++++++++++++++++----- sugar/test/main.rkt | 9 ++++-- typed/sugar/list.rkt | 56 ++++++++++++++++++++++-------------- 4 files changed, 77 insertions(+), 37 deletions(-) diff --git a/sugar/list.rkt b/sugar/list.rkt index 30ec8c5..811f01a 100644 --- a/sugar/list.rkt +++ b/sugar/list.rkt @@ -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))) diff --git a/sugar/scribblings/list.scrbl b/sugar/scribblings/list.scrbl index 773b717..a126b29 100644 --- a/sugar/scribblings/list.scrbl +++ b/sugar/scribblings/list.scrbl @@ -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)) ] diff --git a/sugar/test/main.rkt b/sugar/test/main.rkt index 2148de2..bf7f2de 100644 --- a/sugar/test/main.rkt +++ b/sugar/test/main.rkt @@ -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) diff --git a/typed/sugar/list.rkt b/typed/sugar/list.rkt index 41b3142..529a602 100644 --- a/typed/sugar/list.rkt +++ b/typed/sugar/list.rkt @@ -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)])) +