From e9d457b8ced25f8061869231ab6fe4dacacf0246 Mon Sep 17 00:00:00 2001 From: Brendan Seamas Murphy Date: Sun, 26 Nov 2017 17:18:59 -0800 Subject: [PATCH] Expand data/list --- .../scribblings/hackett/reference.scrbl | 184 ++++++++++++++++-- hackett-lib/hackett/data/list.rkt | 72 ++++++- hackett-lib/hackett/private/prim/base.rkt | 4 +- 3 files changed, 243 insertions(+), 17 deletions(-) diff --git a/hackett-doc/scribblings/hackett/reference.scrbl b/hackett-doc/scribblings/hackett/reference.scrbl index 0716dd8..b6e12b2 100644 --- a/hackett-doc/scribblings/hackett/reference.scrbl +++ b/hackett-doc/scribblings/hackett/reference.scrbl @@ -712,6 +712,22 @@ error, and if @racket[xs] is infinitely long, @racket[init!] will never return. (init! {1 :: 2 :: 3 :: Nil}) (eval:error (init! (: Nil (t:List t:Integer)))))} +@defproc[(inits [xs (t:List a)]) (t:List (t:List a))]{ + +Returns a list of the inital segments of @racket[xs]. + +@(hackett-examples + (inits {1 :: 2 :: 3 :: Nil}) + (inits (: Nil (t:List t:Integer))))} + +@defproc[(tails [xs (t:List a)]) (t:List (t:List a))]{ + +Returns a list of the terminal segments of @racket[xs]. + +@(hackett-examples + (tails {1 :: 2 :: 3 :: Nil}) + (tails (: Nil (t:List t:Integer))))} + @defproc[(uncons [xs (t:List a)]) (t:Maybe (t:Tuple a (t:List a)))]{ When @racket[xs] is @racket[Nil], @racket[uncons xs] is @racket[Nothing]. Otherwise, if @racket[xs] @@ -757,6 +773,16 @@ Produces a list with the first @racket[n] elements of @racket[xs]. If @racket[xs (take 2 {1 :: Nil}) (take 2 (: Nil (t:List t:Integer))))} +@defproc[(take-while [p {a t:-> t:Bool}] [xs (t:List a)]) (t:List a)]{ + +Returns the longest initial segment of @racket[xs] such that every element satisfies @racket[p]. + +@(hackett-examples + (take-while (λ [x] {(remainder! x 2) == 1}) {1 :: 3 :: 6 :: 9 :: Nil}) + (let ([is-just (maybe False (const True))]) + (take-while is-just {(Just 1) :: (Just 2) :: Nothing :: (Just 3) :: Nil})) + (take-while (λ [x] {(remainder! x 2) == 0}) (: Nil (t:List t:Integer))))} + @defproc[(drop [n t:Integer] [xs (t:List a)]) (t:List a)]{ Produces a list like @racket[xs] without its first @racket[n] elements. If @racket[xs] contains fewer @@ -767,6 +793,69 @@ then @racket[n] elements, the result is @racket[Nil]. (drop 2 {1 :: Nil}) (drop 2 (: Nil (t:List t:Integer))))} +@defproc[(drop-while [p {a t:-> t:Bool}] [xs (t:List a)]) (t:List a)]{ + +Returns the longest terminal segment of @racket[xs] which is either @racket[Nil] or starts with an +element @racket[x] such that @racket[(not (p x))]. + +@(hackett-examples + (drop-while (λ [x] {(remainder! x 2) == 1}) {1 :: 3 :: 6 :: 9 :: Nil}) + (let ([is-just (maybe False (const True))]) + (drop-while is-just {(Just 1) :: (Just 2) :: Nothing :: (Just 3) :: Nil})) + (drop-while (λ [x] {(remainder! x 2) == 0}) (: Nil (t:List t:Integer))))} + +@defproc[(nth [xs (t:List a)] [n t:Integer]) (t:Maybe a)]{ + +Returns @racket[Just] the nth element of @racket[xs] if it exists, starting at 0, and +@racket[Nothing] if it doesn't. + +@(hackett-examples + (nth {1 :: 2 :: 3 :: Nil} 2) + (nth {1 :: 2 :: 3 :: Nil} -1) + (nth {1 :: Nil} 2) + (nth (: Nil (t:List t:Integer)) 2))} + +@defproc[(nth! [xs (t:List a)] [n t:Integer]) a]{ + +Returns the nth element of @racket[xs], starting at 0. This function is +@tech[#:key "partial function"]{partial}, because it errors when @racket[{n >= (length xs)}]. + +@(hackett-examples + (nth! {1 :: 2 :: 3 :: Nil} 2) + (eval:error (nth! {1 :: 2 :: 3 :: Nil} -1)) + (eval:error (nth! {1 :: Nil} 2)) + (eval:error (nth! (: Nil (t:List t:Integer)) 2)))} + +@defproc[(find-index [p {a t:-> Bool}] [xs (t:List a)]) (t:Maybe t:Integer)]{ + +Finds the first element of @racket[xs] which satisfies @racket[p], and returns @racket[Just] its +index. If there is no such element, the function returns @racket[Nothing]. + +@(hackett-examples + (find-index (λ [x] {(remainder! x 2) == 0}) {1 :: 2 :: 3 :: Nil}) + (find-index (λ [x] {x < 0}) {1 :: 2 :: 3 :: Nil}) + (find-index (const True) (: Nil (t:List t:Integer))))} + +@defproc[(index-of [_ (Eq a)] [x a] [xs (t:List a)]) (t:Maybe t:Integer)]{ + +Finds the first occurrence of @racket[x] in @racket[xs] and returns @racket[Just] its index. If +@racket[x] is not contained in @racket[xs], the function returns @racket[Nothing]. This is +equivalent to @racket[(find-index (== x) xs)]. + +@(hackett-examples + (index-of 2 {1 :: 2 :: 3 :: Nil}) + (index-of -1 {1 :: 2 :: 3 :: Nil}) + (index-of 2 Nil))} + +@defproc[(find [p {a t:-> t:Bool}] [xs (t:List a)]) (t:Maybe a)]{ + +Returns @racket[Just] the first element @racket[_x] of @racket[xs] such that @racket[(p _x)] is +@racket[True]. If no element in @racket[xs] satisfies @racket[p], @racket[Nothing] is returned. + +@(hackett-examples + (find (λ [x] {x > 5}) {3 :: 7 :: 2 :: 9 :: 12 :: 4 :: Nil}) + (find (λ [x] {x > 5}) {1 :: 2 :: 3 :: Nil}))} + @defproc[(filter [f {a t:-> t:Bool}] [xs (t:List a)]) (t:List a)]{ Produces a list that contains each element, @racket[_x], for which @racket[_x] is an element of @@ -808,6 +897,37 @@ the following expression: (foldl * 1 {1 :: 2 :: 3 :: 4 :: 5 :: Nil}) (foldl - 0 {1 :: 2 :: 3 :: 4 :: 5 :: Nil}))} +@defproc[(unfoldr [step {b t:-> (t:Maybe (t:Tuple a b))}] [seed b]) (t:List a)]{ + +@racket[unfoldr] constructs a list from an initial value, stopping when @racket[(step seed)] is +@racket[Nothing]. In a certain way, @racket[unfoldr] acts as a dual to @racket[foldr]. More +specifically, @racket[{(unfoldr g (foldr f z xs)) == xs}] when @racket[{(g z) == Nothing}] and +@racket[{(g (f x y)) == (Just (tuple x y))}] for any @racket[x] in @racket[xs]. + +@(hackett-examples + (unfoldr (λ [x] (if {x == 1} Nothing + (Just (Tuple (show x) (quotient! x 2))))) + 128) + (take 5 (unfoldr (λ [x] (Just (Tuple x {x + 2}))) 0)))} + +@defproc[(concat [_ (Monoid m)] [ms (t:List m)]) m]{ +Returns the result of appending each element of @racket[ms] together. Equivalent to +@racket[(foldr ++ mempty)]. + +@(hackett-examples + (eval:check (concat {"a" :: "b" :: "c" :: Nil}) "abc") + (eval:check (concat {{1 :: Nil} :: {2 :: 3 :: Nil} :: {4 :: 5 :: 6 :: Nil} :: Nil}) + (:: 1 (:: 2 (:: 3 (:: 4 (:: 5 (:: 6 Nil))))))))} + +@defproc[(fold-map [_ (Monoid m)] [f {a t:-> m}] [xs (List a)]) m]{ + +Applies @racket[f] to each element of @racket[xs] and concatenates each resulting list. Equivalent +to @racket[=<<] when @racket[m] is @racket[(List b)] for some @racket[b]. + +@(hackett-examples + (fold-map show {1 :: 2 :: 3 :: Nil}) + (fold-map tail {{1 :: Nil} :: Nil :: {2 :: 3 :: Nil} :: {4 :: 5 :: 6 :: Nil} :: Nil}))} + @defproc[(sum [xs (t:List t:Integer)]) t:Integer]{ Adds the elements of @racket[xs] together and returns the sum. Equivalent to @racket[(foldl + 0)]. @@ -816,6 +936,23 @@ Adds the elements of @racket[xs] together and returns the sum. Equivalent to @ra (eval:check (sum {1 :: 2 :: 3 :: Nil}) 6) (eval:check (sum Nil) 0))} +@defproc[(product [xs (t:List t:Integer)]) t:Integer]{ + +Multiplies the elements of @racket[xs] together and returns the product. Equivalent to +@racket[(foldl * 1)]. + +@(hackett-examples + (eval:check (product {1 :: 2 :: 3 :: 4 :: Nil}) 24) + (eval:check (product Nil) 1))} + +@defproc[(iterate [step {a t:-> a}] [seed a]) (List a)]{ + +Returns the infinite list @racket[{seed :: (step seed) :: (step (step seed)) :: ...}]. + +@(hackett-examples + (take 5 (iterate (+ 1) 0)) + (take 5 (iterate (λ [x] {x ++ "a"}) "")))} + @defproc[(reverse (xs (t:List a))) (t:List a)]{ Returns @racket[xs] in reversed order. @@ -828,7 +965,7 @@ Returns @racket[xs] in reversed order. This function will apply @racket[f] to each element in @racket[as] and @racket[bs] until it has reached the end of either, then it returns a list like -@racket[{f _a0 _b0 :: f _a1 _b1 :: f _a2 _b2 :: ... :: Nil}] (where @racket[as] contains +@racket[{(f _a0 _b0) :: (f _a1 _b1) :: (f _a2 _b2) :: ... :: Nil}] (where @racket[as] contains elements named @racket[_a0], @racket[_a1], @racket[_a2] etc., and @racket[bs] contains elements named @racket[_b0], @racket[_b1], @racket[_b2] etc.). @@ -857,11 +994,18 @@ Returns an infinite list containing only @racket[x]. @(hackett-examples (take 5 (repeat 1)))} +@defproc[(replicate [n Integer] [x a]) (t:List a)]{ + +Returns a list of @racket[n] copies of @racket[x]. + +@(hackett-examples + (replicate 3 1))} + @defproc[(cycle! [xs (t:List a)]) (t:List a)]{ Returns the infinite list @racket[{xs ++ xs ++ xs ++ ...}]. If @racket[xs] is infinite, -@racket[cycle! xs == xs]. This function is @tech[#:key "partial function"]{partial}, -because it errors when given @racket[Nil]. +@racket[{(cycle! xs)or == xs}]. This function is @tech[#:key "partial function"]{partial}, because it +errors when given @racket[Nil]. @(hackett-examples (take 10 (cycle! {1 :: 2 :: 3 :: Nil})) @@ -871,8 +1015,8 @@ because it errors when given @racket[Nil]. Logically ors the elements of @racket[xs] together and returns the result. Equivalent to @racket[(foldr || False)]. Because it uses a right fold, the only elements which will be evaluated are those before the first expression which -evaluates to @racket[True]. Additionally, @racket[or infinite-list] can never return @racket[False], and -@racket[or (repeat False)] will never terminate. +evaluates to @racket[True]. Additionally, @racket[(or infinite-list)] can never return @racket[False], and +@racket[(or (repeat False))] will never terminate. @(hackett-examples (or {True :: False :: Nil}) @@ -884,8 +1028,8 @@ evaluates to @racket[True]. Additionally, @racket[or infinite-list] can never re Logically ands the elements of @racket[xs] together and returns the result. Equivalent to @racket[(foldr && True)]. Because it uses a right fold, the only elements which will be evaluated are those before the first expression which -evaluates to @racket[False]. Additionally, @racket[and infinite-list] can never return @racket[True], and -@racket[and (repeat True)] will never terminate. +evaluates to @racket[False]. Additionally, @racket[(and infinite-list)] can never return @racket[True], and +@racket[(and (repeat True))] will never terminate. @(hackett-examples (and {True :: False :: Nil}) @@ -947,6 +1091,7 @@ in @racket[xs] will be checked for equality. @(hackett-examples (delete 2 {1 :: 2 :: 3 :: Nil}) + (delete 2 {1 :: 2 :: 3 :: 2 :: Nil}) (delete 0 {1 :: 2 :: 3 :: Nil}) (head (delete 1 {1 :: 2 :: (error! "never happens") :: Nil})) (delete 1 Nil))} @@ -960,13 +1105,14 @@ such @racket[y] will be checked for equality. @(hackett-examples (delete-by > 2 {1 :: 2 :: 3 :: Nil}) (delete-by > 0 {1 :: 2 :: 3 :: Nil}) - (head (delete-by not= 1 {1 :: 2 :: (error! "never happens") :: Nil})) + (head (delete-by /= 1 {1 :: 2 :: (error! "never happens") :: Nil})) (delete-by (λ [y x] {(remainder! y x) == 0}) 2 Nil) (delete-by (error! "never happens") (error! "never happens") (: Nil (t:List t:Integer))))} @defproc[(intersperse [x a] [xs (t:List a)]) (t:List a)]{ -Given a separator and a list, intersperse intersperses the separator between each element of the list. +Given a separator and a list, intersperse returns a new list with the separator placed between each +element of the list. @(hackett-examples (intersperse 42 {1 :: 2 :: 3 :: Nil}) @@ -1035,9 +1181,14 @@ evaluated, produces the value. @subsection[#:tag "reference-equality"]{Equality} @defclass[(t:Eq a) - [== {a t:-> a t:-> t:Bool}]]{ + [== {a t:-> a t:-> t:Bool}] + [/= {a t:-> a t:-> t:Bool}]]{ The class of types with a notion of equality. The @racket[==] method should produce @racket[True] if -both of its arguments are equal, otherwise it should produce @racket[False]. +both of its arguments are equal, otherwise it should produce @racket[False]. The @racket[/=] +method should produce @racket[True] if its arguments are unequal, otherwise it should produce +@racket[False]. Default implementations of @racket[==] and @racket[/=] are given in terms of the +negation of the other, in case inequality is easier to define than equality, or if it is more +efficient to implement independent definitions for each. @defmethod[== {a t:-> a t:-> t:Bool}]{ @@ -1046,7 +1197,16 @@ both of its arguments are equal, otherwise it should produce @racket[False]. (eval:check {10 == 11} False) (eval:check {{1 :: 2 :: Nil} == {1 :: 2 :: Nil}} True) (eval:check {{1 :: 2 :: Nil} == {1 :: Nil}} False) - (eval:check {{1 :: 2 :: Nil} == {1 :: 3 :: Nil}} False))}} + (eval:check {{1 :: 2 :: Nil} == {1 :: 3 :: Nil}} False))} + +@defmethod[/= {a t:-> a t:-> t:Bool}]{ + +@(hackett-examples + (eval:check {10 /= 10} False) + (eval:check {10 /= 11} True) + (eval:check {{1 :: 2 :: Nil} /= {1 :: 2 :: Nil}} False) + (eval:check {{1 :: 2 :: Nil} /= {1 :: Nil}} True) + (eval:check {{1 :: 2 :: Nil} /= {1 :: 3 :: Nil}} True))}} @subsection[#:tag "reference-semigroup-monoid"]{Semigroups and monoids} diff --git a/hackett-lib/hackett/data/list.rkt b/hackett-lib/hackett/data/list.rkt index 0eed6b6..5fe7fce 100644 --- a/hackett-lib/hackett/data/list.rkt +++ b/hackett-lib/hackett/data/list.rkt @@ -3,9 +3,10 @@ (require hackett/data/maybe hackett/private/prim) -(provide (data List) head last tail init head! last! tail! init! uncons uncons! nil? length take drop - filter foldr foldl reverse zip-with zip sum repeat cycle! or and any? all? elem? not-elem? - delete delete-by intersperse) +(provide (data List) head last tail init head! last! tail! init! uncons uncons! unfoldr nil? length + nth nth! find-index index-of take take-while drop drop-while tails inits filter find + foldr foldl reverse zip-with zip sum product iterate repeat replicate cycle! concat + fold-map or and any? all? elem? not-elem? delete delete-by intersperse) (defn head : (∀ [a] {(List a) -> (Maybe a)}) [[{x :: _}] (Just x)] @@ -45,6 +46,11 @@ (defn uncons! : (forall [a] {(List a) -> (Tuple a (List a))}) [[xs] (from-maybe (error! "uncons!: empty list") (uncons xs))]) +(defn unfoldr : (forall [a b] {{b -> (Maybe (Tuple a b))} -> b -> (List a)}) + [[step seed] (case (step seed) + [Nothing Nil] + [(Just (Tuple a b)) {a :: (unfoldr step b)}])]) + (defn nil? : (forall [a] {(List a) -> Bool}) [[Nil] True] [[_ ] False]) @@ -52,6 +58,22 @@ (def length : (forall [a] {(List a) -> Integer}) (foldr (λ [_ acc] {acc + 1}) 0)) +(defn nth : (forall [a] {(List a) -> Integer -> (Maybe a)}) + [[{x :: xs} n] (if {n < 0} Nothing + (if {n == 0} (Just x) + (nth xs {n - 1})))] + [[Nil _] Nothing]) + +(defn nth! : (forall [a] {(List a) -> Integer -> a}) + [[xs n] (from-maybe (error! "nth!: empty list") (nth xs n))]) + +(defn find-index : (forall [a] {{a -> Bool} -> (List a) -> (Maybe Integer)}) + [[p {x :: xs}] (if (p x) (Just 0) (map (+ 1) (find-index p xs)))] + [[_ Nil ] Nothing]) + +(def index-of : (forall [a] (Eq a) => {a -> (List a) -> (Maybe Integer)}) + {find-index . ==}) + (defn take : (∀ [a] {Integer -> (List a) -> (List a)}) [[n {x :: xs}] (if {n == 0} @@ -60,6 +82,14 @@ [[_ Nil] Nil]) +(defn take-while : (∀ [a] {{a -> Bool} -> (List a) -> (List a)}) + [[p {x :: xs}] + (if (p x) + {x :: (take-while p xs)} + Nil)] + [[_ Nil] + Nil]) + (defn drop : (∀ [a] {Integer -> (List a) -> (List a)}) [[n {x :: xs}] (if {n == 0} @@ -68,10 +98,32 @@ [[_ Nil] Nil]) +(defn drop-while : (∀ [a] {{a -> Bool} -> (List a) -> (List a)}) + [[p {x :: xs}] + (if (p x) + (drop-while p xs) + {x :: xs})] + [[_ Nil] + Nil]) + +(def tails : (forall [a] {(List a) -> (List (List a))}) + (λ [l] {l :: (case l + [{x :: xs} (tails xs)] + [Nil Nil])})) + +(def inits : (forall [a] {(List a) -> (List (List a))}) + (λ [l] {Nil :: (case l + [Nil Nil] + [{x :: xs} (map (:: x) (inits xs))])})) + (defn filter : (∀ [a] {{a -> Bool} -> (List a) -> (List a)}) [[f {x :: xs}] (let ([ys (filter f xs)]) (if (f x) {x :: ys} ys))] [[_ Nil ] Nil]) +(defn find : (forall [a] {{a -> Bool} -> (List a) -> (Maybe a)}) + [[p {x :: xs}] (if (p x) (Just x) (find p xs))] + [[p Nil ] Nothing]) + (defn foldl : (∀ [a b] {{b -> a -> b} -> b -> (List a) -> b}) [[f a {x :: xs}] (let ([b (f a x)]) {b seq (foldl f b xs)})] [[_ a Nil ] a]) @@ -89,13 +141,27 @@ (def sum : {(List Integer) -> Integer} (foldl + 0)) +(def product : {(List Integer) -> Integer} + (foldl * 1)) + +(defn iterate : (forall [a] {{a -> a} -> a -> (List a)}) + [[f x] {x :: (iterate f (f x))}]) + (defn repeat : (∀ [a] {a -> (List a)}) [[x] (letrec ([xs {x :: xs}]) xs)]) +(defn replicate : (∀ [a] {Integer -> a -> (List a)}) + [[n x] (if {n <= 0} + Nil + {x :: (replicate {n - 1} x)})]) + (defn cycle! : (∀ [a] {(List a) -> (List a)}) [[Nil] (error! "cycle!: empty list")] [[xs ] (letrec ([ys {xs ++ ys}]) ys)]) +(def fold-map : (forall [a m] (Monoid m) => {{a -> m} -> (List a) -> m}) + (λ [f] (foldr {++ . f} mempty))) + (def or : {(List Bool) -> Bool} (foldr || False)) diff --git a/hackett-lib/hackett/private/prim/base.rkt b/hackett-lib/hackett/private/prim/base.rkt index f3bc95c..30454b0 100644 --- a/hackett-lib/hackett/private/prim/base.rkt +++ b/hackett-lib/hackett/private/prim/base.rkt @@ -119,8 +119,8 @@ (class (Eq a) [== : {a -> a -> Bool} - (λ [a b] (not {a not= b}))] - [not= : {a -> a -> Bool} + (λ [a b] (not {a /= b}))] + [/= : {a -> a -> Bool} (λ [a b] (not {a == b}))]) (instance (Eq Unit)