diff --git a/lib/List.fram b/lib/List.fram index 1e65e391..b2eb018c 100644 --- a/lib/List.fram +++ b/lib/List.fram @@ -56,6 +56,38 @@ pub let tlErr { ~onError } xs = | x :: xs => xs end +{## + Returns the list without the last element or returns `None` if it already + was empty. + ##} +pub let dropLast xs = + let rec dropLastAux y xs = + match xs with + | [] => [] + | x :: xs => y :: dropLastAux x xs + end in + match xs with + | [] => None + | x :: xs => Some (dropLastAux x xs) + end + +{## + Returns the list without the last element or calls `~onError` if it already + was empty. + + @param ~onError Fallback function in case of an empty list. + ##} +pub let dropLastErr { ~onError } xs = + let rec dropLastAux y xs = + match xs with + | [] => [] + | x :: xs => y :: dropLastAux x xs + end in + match xs with + | [] => ~onError () + | x :: xs => dropLastAux x xs + end + {## Returns the n-th element of a list or `None` if n is negative or the list is too short. The first element is at position 0. @@ -82,6 +114,74 @@ pub let nthErr { ~onError } xs (n : Int) = end in if n < 0 then ~onError () else nthErrAux xs n +{## + Returns the last element of a list or `None` if the list is empty. + ##} +pub let last xs = + let rec lastAux y xs = + match xs with + | [] => y + | x :: xs => lastAux x xs + end in + match xs with + | [] => None + | x :: xs => Some (lastAux x xs) + end + +{## + Returns the last element of a list or calls `~onError` if list is empty. + + @param ~onError Fallback for an empty list. + ##} +pub let lastErr { ~onError } xs = + let rec lastErrAux y xs = + match xs with + | [] => y + | x :: xs => lastErrAux x xs + end in + match xs with + | [] => ~onError () + | x :: xs => lastErrAux x xs + end + +{## + Returns pair of combined results of `last` and `dropLast` functions, + but performs this computation in more optimized manner. Returns + `None` for an empty list. + ##} +pub let dropTakeLast xs = + let rec dropTakeLastAux y xs = + match xs with + | [] => ([], y) + | x :: xs => + let (init, last) = dropTakeLastAux x xs in + (y :: init, last) + end in + match xs with + | [] => None + | x :: xs => Some (dropTakeLastAux x xs) + end + +{## + Returns pair of combined results of `last` and `dropLast` functions, + but performs this computation in more optimized manner. Calls `~onError` for + an empty list. + + @param ~onError Fallback for an empty list. + ##} +pub let dropTakeLastErr { ~onError } xs = + let rec dropTakeLastErrAux y xs = + match xs with + | [] => ([], y) + | x :: xs => + let (init, last) = dropTakeLastErrAux x xs in + (y :: init, last) + end in + match xs with + | [] => ~onError () + | x :: xs => dropTakeLastErrAux x xs + end + ## Appends `ys` to `xs`. pub let rec append xs ys = match xs with @@ -332,6 +432,28 @@ pub let rec foldLeft f acc xs = | x :: xs => foldLeft f (f acc x) xs end +{## + `foldLeft1 f [x1, x2, ... ,xn]` is `f (... (f (f x1 x2) x3) ...) xn`. + Returns `None` in case of an empty list. + ##} +pub let rec foldLeft1 f xs = + match xs with + | [] => None + | x :: xs => Some (foldLeft f x xs) + end + +{## + `foldLeft1Err f [x1, x2, ... ,xn]` is `f (... (f (f x1 x2) x3) ...) xn`. + Returns `~onError` in case of an empty list. + + @param ~onError Fallback for an empty list. + ##} +pub let rec foldLeft1Err { ~onError } f xs = + match xs with + | [] => ~onError () + | x :: xs => foldLeft f x xs + end + ## `foldRight f [x1, x2, ..., xn] init` is `f x1 (f x2 (... (f xn init)))`. pub let rec foldRight f xs acc = match xs with @@ -339,6 +461,38 @@ pub let rec foldRight f xs acc = | x :: xs => f x (foldRight f xs acc) end +{## + `foldRight1 f [x1, x2, ..., xn]` is `f x1 (... (f x(n-1) xn)))`. + Returns `None` for an empty list. + ##} +pub let foldRight1 f xs = + let rec foldRight1Aux y xs = + match xs with + | [] => y + | x :: xs => f y (foldRight1Aux x xs) + end in + match xs with + | [] => None + | x :: xs => Some (foldRight1Aux x xs) + end + +{## + `foldRight1Err f [x1, x2, ..., xn]` is `f x1 (... (f x(n-1) xn)))`. + Calls `~onError` for an empty list. + + @param ~onError Fallback for an empty list. + ##} +pub let foldRight1Err { ~onError } f xs = + let rec foldRight1ErrAux y xs = + match xs with + | [] => y + | x :: xs => f y (foldRight1ErrAux x xs) + end in + match xs with + | [] => ~onError () + | x :: xs => foldRight1ErrAux x xs + end + {## `foldLeft2 f init xs ys` works the same as `List.foldLeft` but `f` is applied to consecutive pairs of elements from `xs` and `ys`. @@ -610,8 +764,14 @@ pub method hd = hd pub method hdErr = hdErr pub method tl = tl pub method tlErr = tlErr +pub method dropLast = dropLast +pub method dropLastErr = dropLastErr pub method nth = nth pub method nthErr = nthErr +pub method last = last +pub method lastErr = lastErr +pub method dropTakeLast = dropTakeLast +pub method dropTakeLastErr = dropTakeLastErr pub method append = append pub method add = append pub method revAppend = revAppend @@ -631,7 +791,11 @@ pub method dropWhile self p = dropWhile p self pub method iter self f = iter f self pub method iteri { ?i : Int } self f = iteri { ?i = i } f self pub method foldLeft self f acc = foldLeft f acc self +pub method foldLeft1 self f = foldLeft1 self f +pub method foldLeft1Err self f = foldLeft1Err self f pub method foldRight self f acc = foldRight f self acc +pub method foldRight1 self f = foldRight1 f self +pub method foldRight1Err self f = foldRight1Err f self pub method forAll self p = forAll p self pub method exists self p = exists p self pub method find self p = find p self diff --git a/test/stdlib/stdlib0003_List.fram b/test/stdlib/stdlib0003_List.fram index 06bacbee..38304400 100644 --- a/test/stdlib/stdlib0003_List.fram +++ b/test/stdlib/stdlib0003_List.fram @@ -4,6 +4,12 @@ parameter ~onError let ~onError _ = exit 1 let retEmpty _ = [] +let isNone opt = + match opt with + | Some _ => False + | None => True + end + let xs = [1,2,3,4] let ys = [5,6,7,8] @@ -73,6 +79,18 @@ let _ = assert { msg = "tlErr" } (List.isEmpty (List.tlErr { ~onError = retEmpty } ([] : List Int))) +let _ = + assert {msg = "dropLast"} + (List.dropLast [1, 2, 3] >.unwrapOr [] == [1, 2]) +let _ = assert {msg = "dropLast"} (isNone (List.dropLast ([] : List Int))) + +let _ = + assert {msg = "dropLastErr"} + (List.dropLastErr {~onError=retEmpty} [1, 2, 3] == [1, 2]) +let _ = + assert {msg = "dropLastErr"} + (List.dropLastErr {~onError=retEmpty} ([] : List Int) >.isEmpty) + let _ = assert { msg = "nth" } (match List.nth xs 2 with @@ -90,6 +108,28 @@ let _ = assert { msg = "nthErr" } (List.nthErr {~onError = fn _ => -1} xs 10 == -1) +let _ = assert {msg="last"} (isNone (List.last ([] : List Int))) +let _ = assert {msg="last"} (List.last [1, 2] >.unwrapOr 0 == 2) + +let _ = assert {msg="lastErr"} (List.lastErr {~onError = fn _ => 0} [] == 0) +let _ = assert {msg="lastErr"} (List.lastErr {~onError = fn _ => 0} [1, 2] == 2) + +let _ = assert {msg="dropTakeLast"} + match List.dropTakeLast [1, 2, 3] with + | Some (xs, x) => (3 == x) && ([1, 2] == xs) + | None => False + end +let _ = assert {msg="dropTakeLast"} + (isNone (List.dropTakeLast ([] : List Int))) + +let _ = assert {msg="dropTakeLastErr"} + (let (xs, x) = List.dropTakeLastErr {~onError = fn _ => ([], 0)} [1, 2, 3] + in [1, 2] == xs && 3 == x) + +let _ = assert {msg="dropTakeLastErr"} + (let (xs, x) = List.dropTakeLastErr {~onError = fn _ => ([], 0)} [] + in ([] : List Int) == xs && 0 == x) + let _ = assert { msg = "append" } (xs + ys == [1,2,3,4,5,6,7,8]) let _ = assert { msg = "rev" } (List.rev xs == [4,3,2,1]) @@ -180,9 +220,38 @@ let _ = assert { msg = "init" } (List.init 5 id == [0,1,2,3,4]) let _ = assert { msg = "foldLeft" } (List.foldLeft (fn (a : Int) b => a + b) 0 xs == 10) +let _ = + assert {msg = "foldleft1"} + (List.foldLeft1 (fn (a : Int) b => a + b) xs >.unwrapOr 0 == 10) +let _ = + assert {msg = "foldleft1"} + (List.foldLeft1 (fn (a : Int) b => a + b) ([] : List Int) >.unwrapOr 0 == 0) +let _ = + assert {msg = "foldleft1Err"} + (List.foldLeft1Err {~onError = fn _ => 0} + (fn (a : Int) b => a + b) xs == 10) +let _ = + assert {msg = "foldleft1Err"} + (List.foldLeft1Err {~onError = fn _ => 0} + (fn (a : Int) b => a + b) ([] : List Int) == 0) + let _ = assert { msg = "foldRight" } (List.foldRight (fn (a : Int) b => a + b) xs 0 == 10) +let _ = + assert {msg = "foldRight1"} + (List.foldRight1 (fn (a : Int) b => a + b) xs >.unwrapOr 0 == 10) +let _ = + assert {msg = "foldRight1"} + (List.foldRight1 (fn (a : Int) b => a + b) ([] : List Int) >.unwrapOr 0 == 0) +let _ = + assert {msg = "foldRight1Err"} + (List.foldRight1Err {~onError = fn _ => 0} + (fn (a : Int) b => a + b) xs == 10) +let _ = + assert {msg = "foldRight1Err"} + (List.foldRight1Err {~onError = fn _ => 0} + (fn (a : Int) b => a + b) ([] : List Int) == 0) let _ = assert { msg = "foldLeft2" } (List.foldLeft2 (fn (a : Int) b c => a + b + c) 0 xs ys == 36)