From d92d59cd052132f901a15efc4fa2ad9aedaf88a3 Mon Sep 17 00:00:00 2001 From: Brendan Seamas Murphy Date: Mon, 27 Nov 2017 09:53:19 -0800 Subject: [PATCH 1/3] Expand data/list --- .../scribblings/hackett/reference.scrbl | 101 ++++++++++++++++-- hackett-lib/hackett/data/list.rkt | 43 +++++++- 2 files changed, 134 insertions(+), 10 deletions(-) diff --git a/hackett-doc/scribblings/hackett/reference.scrbl b/hackett-doc/scribblings/hackett/reference.scrbl index ddc9bb6..c3e62ee 100644 --- a/hackett-doc/scribblings/hackett/reference.scrbl +++ b/hackett-doc/scribblings/hackett/reference.scrbl @@ -645,7 +645,17 @@ Returns @racket[Just] the first element of a list, or @racket[Nothing] if the li (head {1 :: 2 :: 3 :: Nil}) (head (: Nil (t:List t:Integer))))} -@defthing[tail (t:forall [a] {(t:List a) t:-> (t:Maybe (t:List a))})]{ +@defthing[last (forall [a] {(List a) -> (Maybe a)})]{ + +Returns @racket[just] the last element of a list, or @racket[nothing] if the list is @racket[nil]. +This function is @tech[#:key "partial function"]{partial}, since it diverges on an infinitely long +input, e.g. @racket[(letrec ([ones {1 :: ones}]) (last ones))]. + +@(hackett-examples + (last {1 :: 2 :: 3 :: nil}) + (last (: nil (List Integer))))} + +@defthing[tail (forall [a] {(List a) -> (Maybe (List a))})]{ Returns @racket[Just] a list without its first element, or @racket[Nothing] if the list is @racket[Nil]. @@ -654,7 +664,17 @@ Returns @racket[Just] a list without its first element, or @racket[Nothing] if t (tail {1 :: 2 :: 3 :: Nil}) (tail (: Nil (t:List t:Integer))))} -@defthing[head! (t:forall [a] {(t:List a) t:-> a})]{ +@defthing[init (forall [a] {(List a) -> (Maybe a)})]{ + +Returns @racket[just] a list without its the last element, or @racket[nothing] if the list is +@racket[nil]. This function is @tech[#:key "partial function"]{partial}, since it diverges on an +infinitely long input, e.g. @racket[(letrec ([ones {1 :: ones}]) (last ones))]. + +@(hackett-examples + (init {1 :: 2 :: 3 :: nil}) + (init (: nil (List Integer))))} + +@defthing[head! (forall [a] {(List a) -> a})]{ A @tech[#:key "partial function"]{partial} version of @racket[head] that returns the first element of a list. If the list is empty, it raises an error. @@ -663,7 +683,17 @@ a list. If the list is empty, it raises an error. (head! {1 :: 2 :: 3 :: Nil}) (eval:error (head! (: Nil (t:List t:Integer)))))} -@defthing[tail! (t:forall [a] {(t:List a) t:-> (t:List a)})]{ +@defthing[last! (forall [a] {(List a) -> a})]{ + +A less-safe version of @racket[last] which tries to return the last element of any list. This function +is @tech[#:key "partial function"]{partial}; if the given list is empty, @racket[last!] raises an +error, and if the list is infinitely long, the function wil not return. + +@(hackett-examples + (last! {1 :: 2 :: 3 :: nil}) + (eval:error (last! (: nil (List Integer)))))} + +@defthing[tail! (forall [a] {(List a) -> (List a)})]{ A @tech[#:key "partial function"]{partial} version of @racket[tail] that returns a list without its first element. If the list is empty, it raises an error. @@ -672,14 +702,60 @@ first element. If the list is empty, it raises an error. (tail! {1 :: 2 :: 3 :: Nil}) (eval:error (tail! (: Nil (t:List t:Integer)))))} -@defproc[(take [n t:Integer] [xs (t:List a)]) (t:List a)]{ +@defthing[init! (forall [a] {(List a) -> a})]{ + +A less-safe version of @racket[init] which tries to return the last element of any list. This function +is @tech[#:key "partial function"]{partial}; if the given list is empty, @racket[init!] raises an +error, and if the list is infinitely long, the function wil not return. + +@(hackett-examples + (init! {1 :: 2 :: 3 :: nil}) + (eval:error (init! (: nil (List Integer)))))} + +@defthing[uncons (forall [a] {(List a) -> (Maybe (Tuple a (List a)))})]{ + +Returns @racket[nothing] if the list is @racket[nil], and @racket[just] a pair of the list's first +element and the rest of it otherwise. + +@(hackett-examples + (uncons {1 :: 2 :: 3 :: nil}) + (uncons (: nil (List Integer))))} + +@defthing[uncons! (forall [a] {(List a) -> (Tuple a (List a))})]{ + +A @tech[#:key "partial function"]{partial} version of @racket[uncons] that returns a pair of the +list's first element and the rest of it. If the list is empty, it raises an error. + +@(hackett-examples + (uncons! {1 :: 2 :: 3 :: nil}) + (eval:error (uncons! (: nil (List Integer)))))} + +@defthing[null? (forall [a] {(List a) -> Bool})]{ + +This predicate is @racket[true] when its argument is of the form @racket[nil], and is false otherwise. + +@(hackett-examples + (null? {1 :: 2 :: 3 :: nil}) + (null? (: nil (List Integer))))} + +@defthing[length (forall [a] {(List a) -> Integer})] + +Returns the length of a finite list. Since the function will diverge on an infinitely long input, +@racket[length] is @tech[#:key "partial function"]{partial}. + +@(hackett-examples + (length {1 :: 2 :: 3 :: nil}) + (length (: nil (List Integer))))} + +@defproc[(take [n Integer] [xs (List a)]) (List a)]{ Produces a list with the first @racket[n] elements of @racket[xs]. If @racket[xs] contains fewer than @racket[n] elements, @racket[xs] is returned unmodified. @(hackett-examples - (take 2 {1 :: 2 :: 3 :: Nil}) - (take 2 {1 :: Nil}))} + (take 2 {1 :: 2 :: 3 :: nil}) + (take 2 {1 :: nil}) + (take 2 (: nil (List Integer))))} @defproc[(drop [n t:Integer] [xs (t:List a)]) (t:List a)]{ @@ -687,8 +763,9 @@ Produces a list like @racket[xs] without its first @racket[n] elements. If @rack then @racket[n] elements, the result is @racket[Nil]. @(hackett-examples - (drop 2 {1 :: 2 :: 3 :: Nil}) - (drop 2 {1 :: Nil}))} + (drop 2 {1 :: 2 :: 3 :: nil}) + (drop 2 {1 :: nil}) + (drop 2 (: nil (List Integer))))} @defproc[(filter [f {a t:-> t:Bool}] [xs (t:List a)]) (t:List a)]{ @@ -739,6 +816,14 @@ 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))} +@defthing[intersperse (forall [a] {a -> (List a) -> (List a)})]{ + +Given a separator and a list, intersperse intersperses the separator between each element of the list. + +@(hackett-examples + (intersperse 42 {1 :: 2 :: 3 :: nil}) + (intersperse 42 nil))} + @section[#:tag "reference-typeclasses"]{Typeclasses} @subsection[#:tag "reference-defining-typeclasses"]{Defining typeclasses and typeclass instances} diff --git a/hackett-lib/hackett/data/list.rkt b/hackett-lib/hackett/data/list.rkt index 47e56a4..e697c8c 100644 --- a/hackett-lib/hackett/data/list.rkt +++ b/hackett-lib/hackett/data/list.rkt @@ -3,23 +3,55 @@ (require hackett/data/maybe hackett/private/prim) -(provide (data List) head tail head! tail! take drop filter foldr foldl reverse zip-with sum - repeat cycle! or and any? all? elem? not-elem? delete delete-by) +(provide (data List) head last tail init head! last! tail! init! uncons uncons! null? length take drop + filter foldr foldl reverse zip-with sum repeat cycle! or and any? all? elem? not-elem? delete + delete-by intersperse) (defn head : (∀ [a] {(List a) -> (Maybe a)}) [[{x :: _}] (Just x)] [[Nil ] Nothing]) +(defn last : (forall [a] {(List a) -> (Maybe a)}) + [[{x :: nil}] (just x)] + [[{_ :: xs} ] (last xs)] + [[_ ] nothing]) + (defn tail : (∀ [a] {(List a) -> (Maybe (List a))}) [[{_ :: xs}] (Just xs)] [[Nil ] Nothing]) +(defn init : (forall [a] {(List a) -> (Maybe (List a))}) + [[nil] nothing] + [[xs ] (just (init! xs))]) + (defn head! : (∀ [a] {(List a) -> a}) [[xs] (from-maybe (error! "head!: empty list") (head xs))]) +(defn last! : (forall [a] {(List a) -> a}) + [[xs] (from-maybe (error! "last!: empty list") (last xs))]) + (defn tail! : (∀ [a] {(List a) -> (List a)}) [[xs] (from-maybe (error! "tail!: empty list") (tail xs))]) +(defn init! : (forall [a] {(List a) -> (List a)}) + [[{_ :: nil}] nil] + [[{x :: xs} ] {x :: (init! xs)}] + [[nil ] (error! "tail!: empty list")]) + +(defn uncons : (forall [a] {(List a) -> (Maybe (Tuple a (List a)))}) + [[{x :: xs}] (just (tuple x xs))] + [[nil ] nothing]) + +(defn uncons! : (forall [a] {(List a) -> (Tuple a (List a))}) + [[xs] (from-maybe (error! "uncons!: empty list") (uncons xs))]) + +(defn null? : (forall [a] {(List a) -> Bool}) + [[nil] true] + [[_ ] false]) + +(def length : (forall [a] {(List a) -> Integer}) + (foldr (λ [_ acc] {acc + 1}) 0)) + (defn take : (∀ [a] {Integer -> (List a) -> (List a)}) [[n {x :: xs}] (if {n == 0} @@ -89,3 +121,10 @@ {y :: (delete-by =? x ys)})] [[_ _ Nil] Nil]) + +(defn intersperse : (forall [a] {a -> (List a) -> (List a)}) + [[_ nil ] nil] + [[in {x :: xs}] (letrec ([helper + (λ* [[{y :: ys}] {in :: y :: (helper ys)}] + [[nil ] nil])]) + {x :: (helper xs)})]) From 6023ae5260e63c7d20bba88cabdfc9ba544a038e Mon Sep 17 00:00:00 2001 From: Brendan Seamas Murphy Date: Sun, 26 Nov 2017 14:51:49 -0800 Subject: [PATCH 2/3] Add functions to data/list and add data/list documentation --- .../scribblings/hackett/reference.scrbl | 270 ++++++++++++++---- hackett-lib/hackett/data/list.rkt | 37 +-- hackett-lib/hackett/private/prim/base.rkt | 5 +- 3 files changed, 233 insertions(+), 79 deletions(-) diff --git a/hackett-doc/scribblings/hackett/reference.scrbl b/hackett-doc/scribblings/hackett/reference.scrbl index c3e62ee..0716dd8 100644 --- a/hackett-doc/scribblings/hackett/reference.scrbl +++ b/hackett-doc/scribblings/hackett/reference.scrbl @@ -637,125 +637,125 @@ The @deftech{list} type, which describes lazy linked lists. Since a list is lazy as long as the entire list is never demanded. The @racket[::] constructor is pronounced “cons”, and it is generally intended to be used infix.} -@defthing[head (t:forall [a] {(t:List a) t:-> (t:Maybe a)})]{ +@defproc[(head [xs (t:List a)]) (t:Maybe a)]{ -Returns @racket[Just] the first element of a list, or @racket[Nothing] if the list is @racket[Nil]. +Returns @racket[Just] the first element of @racket[xs], or @racket[Nothing] if @racket[xs] is @racket[Nil]. @(hackett-examples (head {1 :: 2 :: 3 :: Nil}) (head (: Nil (t:List t:Integer))))} -@defthing[last (forall [a] {(List a) -> (Maybe a)})]{ +@defproc[(last [xs (t:List a)]) (t:Maybe a)]{ -Returns @racket[just] the last element of a list, or @racket[nothing] if the list is @racket[nil]. +Returns @racket[Just] the last element of @racket[xs], or @racket[Nothing] if @racket[xs] is @racket[Nil]. This function is @tech[#:key "partial function"]{partial}, since it diverges on an infinitely long input, e.g. @racket[(letrec ([ones {1 :: ones}]) (last ones))]. @(hackett-examples - (last {1 :: 2 :: 3 :: nil}) - (last (: nil (List Integer))))} + (last {1 :: 2 :: 3 :: Nil}) + (last (: Nil (t:List t:Integer))))} -@defthing[tail (forall [a] {(List a) -> (Maybe (List a))})]{ +@defproc[(tail [xs (t:List a)]) (t:Maybe (t:List a))]{ -Returns @racket[Just] a list without its first element, or @racket[Nothing] if the list is +Returns @racket[Just] @racket[xs] without its first element, or @racket[Nothing] if @racket[xs] is @racket[Nil]. @(hackett-examples (tail {1 :: 2 :: 3 :: Nil}) (tail (: Nil (t:List t:Integer))))} -@defthing[init (forall [a] {(List a) -> (Maybe a)})]{ +@defproc[(init [xs (t:List a)]) (t:Maybe a)]{ -Returns @racket[just] a list without its the last element, or @racket[nothing] if the list is -@racket[nil]. This function is @tech[#:key "partial function"]{partial}, since it diverges on an -infinitely long input, e.g. @racket[(letrec ([ones {1 :: ones}]) (last ones))]. +Returns @racket[Just] @racket[xs] without its the last element, or @racket[Nothing] if @racket[xs] is +@racket[Nil]. This function is @tech[#:key "partial function"]{partial}, since it diverges on an +infinitely long input, e.g. @racket[(letrec ([ones {1 :: ones}]) (init ones))]. @(hackett-examples - (init {1 :: 2 :: 3 :: nil}) - (init (: nil (List Integer))))} + (init {1 :: 2 :: 3 :: Nil}) + (init (: Nil (t:List t:Integer))))} -@defthing[head! (forall [a] {(List a) -> a})]{ +@defproc[(head! [xs (List a)]) a]{ -A @tech[#:key "partial function"]{partial} version of @racket[head] that returns the first element of -a list. If the list is empty, it raises an error. +A @tech[#:key "partial function"]{partial} version of @racket[head] which returns the first element of +@racket[xs]. If @racket[xs] is empty, @racket[head!] raises an error. @(hackett-examples (head! {1 :: 2 :: 3 :: Nil}) (eval:error (head! (: Nil (t:List t:Integer)))))} -@defthing[last! (forall [a] {(List a) -> a})]{ +@defproc[(last! [xs (t:List a)]) a]{ -A less-safe version of @racket[last] which tries to return the last element of any list. This function -is @tech[#:key "partial function"]{partial}; if the given list is empty, @racket[last!] raises an -error, and if the list is infinitely long, the function wil not return. +A less-safe version of @racket[last] which returns the last element of @racket[xs]. This function +is @tech[#:key "partial function"]{partial}, since when @racket[xs] is empty, @racket[last!] raises +an error, and if @racket[xs] is infinitely long, @racket[last!] will never return. @(hackett-examples - (last! {1 :: 2 :: 3 :: nil}) - (eval:error (last! (: nil (List Integer)))))} + (last! {1 :: 2 :: 3 :: Nil}) + (eval:error (last! (: Nil (t:List t:Integer)))))} -@defthing[tail! (forall [a] {(List a) -> (List a)})]{ +@defproc[(tail! [xs (t:List a)]) (t:List a)]{ -A @tech[#:key "partial function"]{partial} version of @racket[tail] that returns a list without its -first element. If the list is empty, it raises an error. +A @tech[#:key "partial function"]{partial} version of @racket[tail] that returns @racket[xs] without +its first element. If @racket[xs] is empty, @racket[tail!] raises an error. @(hackett-examples (tail! {1 :: 2 :: 3 :: Nil}) (eval:error (tail! (: Nil (t:List t:Integer)))))} -@defthing[init! (forall [a] {(List a) -> a})]{ +@defproc[(init! [xs (t:List a)]) a]{ -A less-safe version of @racket[init] which tries to return the last element of any list. This function -is @tech[#:key "partial function"]{partial}; if the given list is empty, @racket[init!] raises an -error, and if the list is infinitely long, the function wil not return. +A less-safe version of @racket[init] which returns the last element of @racket[xs]. This function +is @tech[#:key "partial function"]{partial}, since when @racket[xs] is empty, @racket[init!] raises an +error, and if @racket[xs] is infinitely long, @racket[init!] will never return. @(hackett-examples - (init! {1 :: 2 :: 3 :: nil}) - (eval:error (init! (: nil (List Integer)))))} + (init! {1 :: 2 :: 3 :: Nil}) + (eval:error (init! (: Nil (t:List t:Integer)))))} -@defthing[uncons (forall [a] {(List a) -> (Maybe (Tuple a (List a)))})]{ +@defproc[(uncons [xs (t:List a)]) (t:Maybe (t:Tuple a (t:List a)))]{ -Returns @racket[nothing] if the list is @racket[nil], and @racket[just] a pair of the list's first -element and the rest of it otherwise. +When @racket[xs] is @racket[Nil], @racket[uncons xs] is @racket[Nothing]. Otherwise, if @racket[xs] +is @racket[{y :: ys}] then @racket[uncons xs] is @racket[Just (Tuple y ys)]. @(hackett-examples - (uncons {1 :: 2 :: 3 :: nil}) - (uncons (: nil (List Integer))))} + (uncons {1 :: 2 :: 3 :: Nil}) + (uncons (: Nil (t:List t:Integer))))} -@defthing[uncons! (forall [a] {(List a) -> (Tuple a (List a))})]{ +@defproc[(uncons! [xs (t:List a)]) (t:Tuple a (t:List a))]{ -A @tech[#:key "partial function"]{partial} version of @racket[uncons] that returns a pair of the -list's first element and the rest of it. If the list is empty, it raises an error. +A @tech[#:key "partial function"]{partial} version of @racket[uncons] that returns +@racket[Tuple (head! xs) (tail! xs)]. If @racket[xs] is empty, it instead raises an error. @(hackett-examples - (uncons! {1 :: 2 :: 3 :: nil}) - (eval:error (uncons! (: nil (List Integer)))))} + (uncons! {1 :: 2 :: 3 :: Nil}) + (eval:error (uncons! (: Nil (t:List t:Integer)))))} -@defthing[null? (forall [a] {(List a) -> Bool})]{ +@defproc[(nil? [xs (t:List a)]) (t:Bool)]{ -This predicate is @racket[true] when its argument is of the form @racket[nil], and is false otherwise. +This predicate is @racket[True] when @racket[xs] is of the form @racket[Nil], and is false otherwise. @(hackett-examples - (null? {1 :: 2 :: 3 :: nil}) - (null? (: nil (List Integer))))} + (nil? {1 :: 2 :: 3 :: Nil}) + (nil? (: Nil (t:List t:Integer))))} -@defthing[length (forall [a] {(List a) -> Integer})] +@defproc[(length [xs (t:List a)]) t:Integer]{ -Returns the length of a finite list. Since the function will diverge on an infinitely long input, -@racket[length] is @tech[#:key "partial function"]{partial}. +Returns the length of @racket[xs] when @racket[xs] is finite. Since the function diverges on an +infinitely long input, @racket[length] is @tech[#:key "partial function"]{partial}. @(hackett-examples - (length {1 :: 2 :: 3 :: nil}) - (length (: nil (List Integer))))} + (length {1 :: 2 :: 3 :: Nil}) + (length (: Nil (t:List t:Integer))))} -@defproc[(take [n Integer] [xs (List a)]) (List a)]{ +@defproc[(take [n t:Integer] [xs (t:List a)]) (t:List a)]{ Produces a list with the first @racket[n] elements of @racket[xs]. If @racket[xs] contains fewer than @racket[n] elements, @racket[xs] is returned unmodified. @(hackett-examples - (take 2 {1 :: 2 :: 3 :: nil}) - (take 2 {1 :: nil}) - (take 2 (: nil (List Integer))))} + (take 2 {1 :: 2 :: 3 :: Nil}) + (take 2 {1 :: Nil}) + (take 2 (: Nil (t:List t:Integer))))} @defproc[(drop [n t:Integer] [xs (t:List a)]) (t:List a)]{ @@ -763,9 +763,9 @@ Produces a list like @racket[xs] without its first @racket[n] elements. If @rack then @racket[n] elements, the result is @racket[Nil]. @(hackett-examples - (drop 2 {1 :: 2 :: 3 :: nil}) - (drop 2 {1 :: nil}) - (drop 2 (: nil (List Integer))))} + (drop 2 {1 :: 2 :: 3 :: Nil}) + (drop 2 {1 :: Nil}) + (drop 2 (: Nil (t:List t:Integer))))} @defproc[(filter [f {a t:-> t:Bool}] [xs (t:List a)]) (t:List a)]{ @@ -816,13 +816,161 @@ 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))} -@defthing[intersperse (forall [a] {a -> (List a) -> (List a)})]{ +@defproc[(reverse (xs (t:List a))) (t:List a)]{ + +Returns @racket[xs] in reversed order. + +@(hackett-examples + (reverse {1 :: 2 :: 3 :: Nil}) + (reverse (: Nil (t:List t:Integer))))} + +@defproc[(zip-with [f {a t:-> b t:-> c}] [as (t:List a)] [bs (t:List b)]) (t:List c)]{ + +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 +elements named @racket[_a0], @racket[_a1], @racket[_a2] etc., and @racket[bs] contains elements +named @racket[_b0], @racket[_b1], @racket[_b2] etc.). + +@(hackett-examples + (zip-with + {1 :: 2 :: 3 :: Nil} {18 :: 42 :: 50 :: Nil}) + (zip-with + {1 :: 2 :: 3 :: Nil} {18 :: 42 :: 50 :: 100 :: Nil}) + (zip-with + {1 :: 2 :: 3 :: 4 :: Nil} {18 :: 42 :: 50 :: Nil}) + (zip-with * {1 :: 2 :: 3 :: Nil} {18 :: 42 :: 50 :: Nil}) + (zip-with + (: Nil (t:List t:Integer)) (: Nil (t:List t:Integer))))} + +@defproc[(zip [as (t:List a)] [bs (t:List b)]) (t:List (t:Tuple a b))]{ + +Returns a list of componenetwise pairs from @racket[as] and @racket[bs]. The length of this list is +the length of the shortest input list. Equivalent to @racket[(zip-with Tuple as bs)]. + +@(hackett-examples + (zip {1 :: 2 :: 3 :: Nil} {18 :: 42 :: 50 :: Nil}) + (zip {1 :: 2 :: 3 :: Nil} {18 :: 42 :: 50 :: 100 :: Nil}) + (zip {1 :: 2 :: 3 :: 4 :: Nil} {18 :: 42 :: 50 :: Nil}) + (zip (: Nil (t:List t:Integer)) (: Nil (t:List t:Integer))))} + +@defproc[(repeat [x a]) (t:List a)]{ + +Returns an infinite list containing only @racket[x]. + +@(hackett-examples + (take 5 (repeat 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]. + +@(hackett-examples + (take 10 (cycle! {1 :: 2 :: 3 :: Nil})) + (eval:error (cycle! (: Nil (t:List t:Integer)))))} + +@defproc[(or [xs (t:List t:Bool)]) t:Bool]{ + +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. + +@(hackett-examples + (or {True :: False :: Nil}) + (or {False :: True :: Nil}) + (or {True :: (error! "never happens") :: Nil}) + (or Nil))} + +@defproc[(and [xs (t:List t:Bool)]) t:Bool]{ + +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. + +@(hackett-examples + (and {True :: False :: Nil}) + (and {False :: True :: Nil}) + (and {False :: (error! "never happens") :: Nil}) + (and Nil))} + +@defproc[(any? [p {a t:-> t:Bool}] [xs (t:List t:Bool)]) t:Bool]{ + +Returns @racket[True] if @racket[xs] contains an element @racket[x] such that @racket[(p x)] is +@racket[True]. Equivalent to @racket[(or (map p xs))]. + +@(hackett-examples + (any? (<= 2) {1 :: 2 :: 3 :: Nil}) + (any? (<= 4) {1 :: 2 :: 3 :: Nil}) + (any? (<= 0) {1 :: (error! "never happens") :: Nil}) + (any? (<= 0) Nil))} + +@defproc[(all? [p {a t:-> t:Bool}] [xs (t:List t:Bool)]) t:Bool]{ + +Returns @racket[True] if for every element @racket[x] of @racket[xs] @racket[(p x)] is +@racket[True]. Equivalent to @racket[(and (map p xs))]. + +@(hackett-examples + (all? (<= 1) {1 :: 2 :: 3 :: Nil}) + (all? (<= 2) {1 :: 2 :: 3 :: Nil}) + (all? (<= 2) {1 :: (error! "never happens") :: Nil}) + (all? (<= 1) Nil))} + +@defproc[(elem? [_ (t:Eq a)] [x a] [xs (t:List a)]) t:Bool]{ + +Returns @racket[True] if @racket[xs] contains @racket[x]. Equivalent to @racket[(any? (== x) xs)]. +Only elements to the left of the first expression equal to @racket[x] in @racket[xs] will be checked +for equality. + +@(hackett-examples + (elem? 2 {1 :: 2 :: 3 :: Nil}) + (elem? 0 {1 :: 2 :: 3 :: Nil}) + (elem? 1 {1 :: (error! "never happens") :: Nil}) + (elem? 1 Nil))} + +@defproc[(not-elem? [_ (t:Eq a)] [x a] [xs (t:List a)]) t:Bool]{ + +Returns @racket[False] if @racket[xs] contains @racket[x]. Equivalent to @racket[(not (elem? x xs))]. +Only elements to the left of the first expression equal to @racket[x] in @racket[xs] will be checked +for equality. + +@(hackett-examples + (elem? 2 {1 :: 2 :: 3 :: Nil}) + (elem? 0 {1 :: 2 :: 3 :: Nil}) + (elem? 1 {1 :: (error! "never happens") :: Nil}) + (elem? 1 Nil))} + +@defproc[(delete [_ (t:Eq a)] [x a] [xs (t:List a)]) (t:List a)]{ + +Returns @racket[xs] with the first occurrence of @racket[x] removed. Equivalent to +@racket[(delete-by == x xs)]. Only elements to the left of the first expression equal to @racket[x] +in @racket[xs] will be checked for equality. + +@(hackett-examples + (delete 2 {1 :: 2 :: 3 :: Nil}) + (delete 0 {1 :: 2 :: 3 :: Nil}) + (head (delete 1 {1 :: 2 :: (error! "never happens") :: Nil})) + (delete 1 Nil))} + +@defproc[(delete-by [rel {a t:-> a t:-> t:Bool}] [x a] [xs (t:List a)]) (t:List a)]{ + +Finds the first element @racket[y] such that @racket[{y rel x}] and returns @racket[xs] with +@racket[y] removed. Generalizes @racket[delete]. Only elements to the left of the first expression +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})) + (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. @(hackett-examples - (intersperse 42 {1 :: 2 :: 3 :: nil}) - (intersperse 42 nil))} + (intersperse 42 {1 :: 2 :: 3 :: Nil}) + (intersperse 42 Nil))} @section[#:tag "reference-typeclasses"]{Typeclasses} diff --git a/hackett-lib/hackett/data/list.rkt b/hackett-lib/hackett/data/list.rkt index e697c8c..0eed6b6 100644 --- a/hackett-lib/hackett/data/list.rkt +++ b/hackett-lib/hackett/data/list.rkt @@ -3,26 +3,26 @@ (require hackett/data/maybe hackett/private/prim) -(provide (data List) head last tail init head! last! tail! init! uncons uncons! null? length take drop - filter foldr foldl reverse zip-with 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! 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) (defn head : (∀ [a] {(List a) -> (Maybe a)}) [[{x :: _}] (Just x)] [[Nil ] Nothing]) (defn last : (forall [a] {(List a) -> (Maybe a)}) - [[{x :: nil}] (just x)] + [[{x :: Nil}] (Just x)] [[{_ :: xs} ] (last xs)] - [[_ ] nothing]) + [[_ ] Nothing]) (defn tail : (∀ [a] {(List a) -> (Maybe (List a))}) [[{_ :: xs}] (Just xs)] [[Nil ] Nothing]) (defn init : (forall [a] {(List a) -> (Maybe (List a))}) - [[nil] nothing] - [[xs ] (just (init! xs))]) + [[Nil] Nothing] + [[xs ] (Just (init! xs))]) (defn head! : (∀ [a] {(List a) -> a}) [[xs] (from-maybe (error! "head!: empty list") (head xs))]) @@ -34,20 +34,20 @@ [[xs] (from-maybe (error! "tail!: empty list") (tail xs))]) (defn init! : (forall [a] {(List a) -> (List a)}) - [[{_ :: nil}] nil] + [[{_ :: Nil}] Nil] [[{x :: xs} ] {x :: (init! xs)}] - [[nil ] (error! "tail!: empty list")]) + [[Nil ] (error! "tail!: empty list")]) (defn uncons : (forall [a] {(List a) -> (Maybe (Tuple a (List a)))}) - [[{x :: xs}] (just (tuple x xs))] - [[nil ] nothing]) + [[{x :: xs}] (Just (Tuple x xs))] + [[Nil ] Nothing]) (defn uncons! : (forall [a] {(List a) -> (Tuple a (List a))}) [[xs] (from-maybe (error! "uncons!: empty list") (uncons xs))]) -(defn null? : (forall [a] {(List a) -> Bool}) - [[nil] true] - [[_ ] false]) +(defn nil? : (forall [a] {(List a) -> Bool}) + [[Nil] True] + [[_ ] False]) (def length : (forall [a] {(List a) -> Integer}) (foldr (λ [_ acc] {acc + 1}) 0)) @@ -83,6 +83,9 @@ [[f {x :: xs} {y :: ys}] {(f x y) :: (zip-with f xs ys)}] [[_ _ _ ] Nil]) +(def zip : (∀ [a b] {(List a) -> (List b) -> (List (Tuple a b))}) + (zip-with Tuple)) + (def sum : {(List Integer) -> Integer} (foldl + 0)) @@ -109,7 +112,7 @@ [[x] (any? (== x))]) (defn not-elem? : (∀ [a] (Eq a) => {a -> (List a) -> Bool}) - [[x] (all? {not . (== x)})]) + [[x] {not . (elem? x)}]) (def delete : (∀ [a] (Eq a) => {a -> (List a) -> (List a)}) (delete-by ==)) @@ -123,8 +126,8 @@ Nil]) (defn intersperse : (forall [a] {a -> (List a) -> (List a)}) - [[_ nil ] nil] + [[_ Nil ] Nil] [[in {x :: xs}] (letrec ([helper (λ* [[{y :: ys}] {in :: y :: (helper ys)}] - [[nil ] nil])]) + [[Nil ] Nil])]) {x :: (helper xs)})]) diff --git a/hackett-lib/hackett/private/prim/base.rkt b/hackett-lib/hackett/private/prim/base.rkt index 5cdafba..f3bc95c 100644 --- a/hackett-lib/hackett/private/prim/base.rkt +++ b/hackett-lib/hackett/private/prim/base.rkt @@ -118,7 +118,10 @@ ;; Eq (class (Eq a) - [== : {a -> a -> Bool}]) + [== : {a -> a -> Bool} + (λ [a b] (not {a not= b}))] + [not= : {a -> a -> Bool} + (λ [a b] (not {a == b}))]) (instance (Eq Unit) [== (λ [Unit Unit] True)]) From e9d457b8ced25f8061869231ab6fe4dacacf0246 Mon Sep 17 00:00:00 2001 From: Brendan Seamas Murphy Date: Sun, 26 Nov 2017 17:18:59 -0800 Subject: [PATCH 3/3] 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)