diff --git a/list/list.mbt b/list/list.mbt index 80609d856..480f3eb0d 100644 --- a/list/list.mbt +++ b/list/list.mbt @@ -818,3 +818,193 @@ test "lookup" { @assertion.assert_eq(ls.lookup(3), Some("c"))? @assertion.assert_eq(ls.lookup(4), None)? } + +/// Returns the first element of list that satisfies f. +/// +/// # Example +/// +/// ``` +/// println(List::[1, 3, 5, 8].find(fn(element) -> Bool { element % 2 == 0})) +/// // output: 8 +/// ``` +pub fn find[T](self : List[T], f : (T) -> Bool) -> T { + match self { + Nil => abort("find: no matching element") + Cons(element, list) => if f(element) { element } else { list.find(f) } + } +} + +test "find" { + @assertion.assert_eq( + List::[1, 3, 5, 8].find(fn(element) -> Bool { element % 2 == 0 }), + 8, + )? +} + +/// Find the first element in the list that satisfies f. +/// +/// # Example +/// +/// ``` +/// println(List::[1, 3, 5, 8].find(fn(element) -> Bool { element % 2 == 0})) +/// // output: Some(8) +/// ``` +pub fn find_option[T](self : List[T], f : (T) -> Bool) -> Option[T] { + match self { + Nil => None + Cons(element, list) => + if f(element) { + Some(element) + } else { + list.find_option(f) + } + } +} + +test "find_option" { + @assertion.assert_eq( + List::[1, 3, 5, 8].find_option(fn(element) -> Bool { element % 2 == 0 }), + Some(8), + )? + @assertion.assert_eq( + List::[1, 3, 5, 7].find_option(fn(element) -> Bool { element % 2 == 0 }), + None, + )? +} + +/// Find the first element in the list that satisfies f and passes the index as an argument. +/// +/// # Example +/// +/// ``` +/// println(List::[1, 3, 5, 8].findi(fn(element, i) -> Bool { (element % 2 == 0) && (i == 3) })) +/// // output: 8 +/// ``` +pub fn findi[T](self : List[T], f : (T, Int) -> Bool) -> T { + fn find(list : List[T], index : Int) -> T { + match list { + Nil => abort("findi: no matching element") + Cons(element, list) => + if f(element, index) { + element + } else { + find(list, index + 1) + } + } + } + + find(self, 0) +} + +test "findi" { + @assertion.assert_eq( + List::[1, 3, 5, 8].findi( + fn(element, i) -> Bool { element % 2 == 0 && i == 3 }, + ), + 8, + )? +} + +/// Find the first element in the list that satisfies f and passes the index as an argument. +/// +/// # Example +/// +/// ``` +/// println(List::[1, 3, 5, 8].findi_option(fn(element) -> Bool { (element % 2 == 0) && (i == 3) })) +/// // output: Some(8) +/// +/// println(List::[1, 3, 8, 5].findi_option(fn(element) -> Bool { (element % 2 == 0) && (i == 3) })) +/// // output: None +/// ``` +pub fn findi_option[T](self : List[T], f : (T, Int) -> Bool) -> Option[T] { + fn find_option(list : List[T], index : Int) -> Option[T] { + match list { + Nil => None + Cons(element, list) => + if f(element, index) { + Some(element) + } else { + find_option(list, index + 1) + } + } + } + + find_option(self, 0) +} + +test "findi_option" { + @assertion.assert_eq( + List::[1, 3, 5, 8].findi_option( + fn(element, i) -> Bool { element % 2 == 0 && i == 3 }, + ), + Some(8), + )? + @assertion.assert_eq( + List::[1, 3, 8, 5].findi_option( + fn(element, i) -> Bool { element % 2 == 0 && i == 3 }, + ), + None, + )? +} + +/// Returns true if list starts with prefix. +/// +/// # Example +/// +/// ``` +/// println(List::[1, 2, 3, 4, 5].is_prefix(List::[1, 2, 3])) +/// // output: true +/// ``` +pub fn is_prefix[T : Eq](self : List[T], prefix : List[T]) -> Bool { + match prefix { + Nil => true + Cons(h, t) => + match self { + Nil => false + Cons(h1, t1) => h == h1 && t1.is_prefix(t) + } + } +} + +test "is_prefix" { + @assertion.assert_true(List::[1, 2, 3, 4, 5].is_prefix(List::[1, 2, 3]))? + @assertion.assert_false(List::[1, 2, 3, 4, 5].is_prefix(List::[3, 2, 3]))? +} + +/// Compares two lists for equality. +/// +/// # Example +/// +/// ``` +/// println(List::[1, 2, 3].equal(List::[1, 2, 3])) +/// // output: true +/// ``` +pub fn equal[T : Eq](self : List[T], other : List[T]) -> Bool { + match (self, other) { + (Nil, Nil) => true + (Cons(h, t), Cons(h1, t1)) => if h == h1 { t.equal(t1) } else { false } + _ => false + } +} + +test "equal" { + @assertion.assert_true(List::[1, 2, 3].equal(List::[1, 2, 3]))? + @assertion.assert_false(List::[1, 2, 3].equal(List::[1, 3, 3]))? +} + +/// Returns true if list ends with suffix. +/// +/// # Example +/// +/// ``` +/// println(List::[1, 2, 3, 4, 5].is_suffix(List::[3, 4, 5])) +/// // output: true +/// ``` +pub fn is_suffix[T : Eq](self : List[T], suffix : List[T]) -> Bool { + self.reverse().is_prefix(suffix.reverse()) +} + +test "is_suffix" { + @assertion.assert_true(List::[1, 2, 3, 4, 5].is_suffix(List::[3, 4, 5]))? + @assertion.assert_false(List::[1, 2, 3, 4, 5].is_suffix(List::[3, 4, 6]))? +} diff --git a/stack/stack.mbt b/stack/stack.mbt index f4580522d..0b3e462de 100644 --- a/stack/stack.mbt +++ b/stack/stack.mbt @@ -189,7 +189,7 @@ test "push_list" { @assertion.assert_eq(s.len, 3)? } -/// Push other stack into the current stack` +/// Push other stack into the current stack /// /// # Example /// @@ -200,7 +200,7 @@ test "push_list" { /// println(s1) // output: {elements: Cons(1, Cons(2, Cons(3, Nil))), len: 3} /// ``` pub fn push_stack[T](self : Stack[T], stack : Stack[T]) -> Unit { - stack.iter(fn(i) -> Unit { self.push(i) }) + stack.elements.iter(fn(i) -> Unit { self.push(i) }) } test "push_stack" { @@ -425,42 +425,6 @@ pub fn length[T](self : Stack[T]) -> Int { self.len } -/// Iterates over the stack. -/// -/// # Example -/// -/// ``` -/// Stack::from_array([1, 2, 3, 4, 5]).iter(print) // output: 54321 -/// ``` -pub fn iter[T](self : Stack[T], f : (T) -> Unit) -> Unit { - self.elements.iter(f) -} - -test "iter" { - let s : Stack[Int] = Stack::[5, 4, 3, 2, 1] - let mut i = 0 - let mut failed = false - s.iter(fn(x) { i = i + 1; if x != i { failed = true } }) - if failed { - return Err("iter test failed") - } -} - -/// Iterates over the stack with index. -pub fn iteri[T](self : Stack[T], f : (Int, T) -> Unit) { - self.elements.iteri(f) -} - -test "iteri" { - let mut v = 0 - let mut failed = false - let s = Stack::[5, 4, 3, 2, 1] - s.iteri(fn(i, x) { if x != v + 1 || i != v { failed = true }; v = v + 1 }) - if failed { - return Err("iteri test failed") - } -} - /// Convert stack to list /// /// # Example @@ -494,116 +458,6 @@ test "to_array" { @assertion.assert_eq(Stack::[3, 2, 1].to_array(), [1, 2, 3])? } -/// Fold the stack. -/// -/// # Example -/// -/// ``` -/// let s = Stack::from_array([1, 2, 3, 4, 5]).fold(0, fn(acc, x) { acc + x }) -/// println(s) // output: 15 -/// ``` -pub fn fold[T, U](self : Stack[T], initial : U, f : (U, T) -> U) -> U { - self.elements.fold(initial, f) -} - -test "fold" { - @assertion.assert_eq( - Stack::[1, 2, 3, 4, 5].fold(0, fn(acc, x) { acc + x }), - 15, - )? -} - -/// Filter the stack. -/// -/// # Example -/// -/// ``` -/// println(Stack::from_array([1, 2, 3, 4, 5]).filter(fn(x){ x % 2 == 0})) -/// // output: Cons(4, Cons(2, Nil)) -/// ``` -pub fn filter[T](self : Stack[T], f : (T) -> Bool) -> Stack[T] { - let elements = self.elements.filter(f) - { elements, len: elements.length() } -} - -test "filter" { - let s = Stack::[1, 2, 3, 4, 5] - let s2 = s.filter(fn(x) { x % 2 == 0 }) - @assertion.assert_eq(s2.elements, Cons(4, Cons(2, Nil)))? - @assertion.assert_eq(s2.len, 2)? -} - -/// Maps the stack. -/// -/// # Example -/// -/// ``` -/// println(Stack::from_array([1, 2, 3]).map(fn(x){ x * 2}))) -/// // output: { elements: Cons(6, Cons(4, Cons(2, Nil))), len: 3 } -/// ``` -pub fn map[T, U](self : Stack[T], f : (T) -> U) -> Stack[U] { - { elements: self.elements.map(f), len: self.len } -} - -test "map" { - let s = Stack::[1, 2, 3] - @assertion.assert_eq( - s.map(fn(x) { x * 2 }).elements, - Cons(6, Cons(4, Cons(2, Nil))), - )? -} - -/// Checks if all elements of the stack satisfy the predicate f. -/// If the stack is empty, return true. -/// -/// NOTE: Since the current standard library List lacks the forall function, -/// this function internally implements the forall function of List. -/// -/// # Example -/// -/// ``` -/// println(Stack::[2, 4, 6].forall(fn(element) -> Bool { element % 2 == 0 })) -/// // output: true -/// ``` -pub fn forall[T](self : Stack[T], f : (T) -> Bool) -> Bool { - fn list_forall(list : List[T]) -> Bool { - match list { - Nil => true - Cons(h, t) => if f(h) { list_forall(t) } else { false } - } - } - - list_forall(self.elements) -} - -test "forall" { - @assertion.assert_true( - Stack::[2, 4, 6].forall(fn(element) -> Bool { element % 2 == 0 }), - )? - @assertion.assert_false( - Stack::[1, 3, 5].forall(fn(element) -> Bool { element % 2 == 0 }), - )? -} - -/// Reverse stack -/// -/// # Example -/// -/// ``` -/// println(Stack::[2, 4, 6].reverse()) -/// // output: Cons(2, Cons(4, Cons(6, Nil))) -/// ``` -pub fn reverse[T](self : Stack[T]) -> Stack[T] { - { elements: self.elements.reverse(), len: self.len } -} - -test "reverse" { - @assertion.assert_eq( - Stack::[2, 4, 6].reverse().elements, - Cons(2, Cons(4, Cons(6, Nil))), - )? -} - /// Compare two stacks. /// /// NOTE: Since the current standard library List lacks the equal or op_equal function, @@ -616,21 +470,8 @@ test "reverse" { /// // output: true /// ``` pub fn equal[T : Eq](self : Stack[T], other : Stack[T]) -> Bool { - fn list_eq(list1 : List[T], list2 : List[T]) -> Bool { - match (list1, list2) { - (Nil, Nil) => true - (Cons(h1, t1), Cons(h2, t2)) => - if h1 == h2 { - list_eq(t1, t2) - } else { - false - } - _ => false - } - } - if self.len == other.len { - list_eq(self.elements, other.elements) + self.elements.equal(other.elements) } else { false }