diff --git a/array/array.mbt b/array/array.mbt index a72cdbbc2..683ba0da4 100644 --- a/array/array.mbt +++ b/array/array.mbt @@ -78,6 +78,18 @@ test "iteri" { @assertion.assert_false(failed)? } +/// Iterates over each element in reversed turn. +/// +/// # Arguments +/// +/// - `self`: The array to iterate over. +/// - `f`: The function to apply to each element. +/// +/// # Example +/// +/// ``` +/// [1, 2, 3, 4, 5].iter(fn(x){ print("\(x) ") }) //output: 5 4 3 2 1 +/// ``` pub fn iter_rev[T](self : Array[T], f : (T) -> Unit) -> Unit { for i = self.length() - 1; i >= 0; i = i - 1 { f(self[i]) @@ -98,6 +110,20 @@ test "iter_rev" { @assertion.assert_false(failed)? } +/// Iterates over the array with index in reversed turn. +/// +/// # Arguments +/// +/// - `self`: The array to iterate over. +/// - `f`: A function that takes an `Int` representing the index and a `T` representing the element of the array, and returns `Unit`. +/// +/// # Example +/// +/// ``` +/// [1, 2, 3, 4, 5].iteri(fn(index, elem){ +/// print("(\(index),\(elem)) ") +/// }) //output: (4,5) (3,4) (2,3) (1,2) (0,1) +/// ``` pub fn iteri_rev[T](self : Array[T], f : (Int, T) -> Unit) -> Unit { for i = self.length() - 1; i >= 0; i = i - 1 { f(i, self[i]) @@ -146,7 +172,14 @@ test "map" { @assertion.assert_eq(doubled, [2, 4, 6, 8, 10])? } -pub fn map_with_index[T, U](self : Array[T], f : (T, Int) -> U) -> Array[U] { +/// Maps a function over the elements of the arr with index. +/// +/// # Example +/// ``` +/// let v = [3, 4, 5] +/// v.mapi(fn (i, x) {x + i}) // [3, 5, 6] +/// ``` +pub fn mapi[T, U](self : Array[T], f : (T, Int) -> U) -> Array[U] { if self.length() == 0 { return Array::default() } @@ -159,8 +192,8 @@ pub fn map_with_index[T, U](self : Array[T], f : (T, Int) -> U) -> Array[U] { test "map_with_index" { let arr = [1, 2, 3, 4, 5] - let doubled = arr.map_with_index(fn(x, i) { x * 2 + i }) - let empty : Array[Int] = Array::default().map_with_index(fn(x, i) { x + i }) + let doubled = arr.mapi(fn(x, i) { x * 2 + i }) + let empty : Array[Int] = Array::default().mapi(fn(x, i) { x + i }) @assertion.assert_eq(empty, [])? @assertion.assert_eq(doubled, [2, 5, 8, 11, 14])? } @@ -208,7 +241,7 @@ pub fn new_with_index[T](length : Int, value : (Int) -> T) -> Array[T] { } } -test "new index" { +test "new_index" { let arr = new_with_index(2, fn { i => i }) @assertion.assert_eq(arr[0], 0)? @assertion.assert_eq(arr[1], 1)? @@ -224,6 +257,13 @@ test "from_array" { @assertion.assert_eq(array, [1, 2, 3, 4, 5])? } +/// Fold out values from an array according to certain rules. +/// +/// # Example +/// ``` +/// let sum = [1, 2, 3, 4, 5].fold(~init=0, fn { sum, elem => sum + elem }) +/// sum // 15 +/// ``` pub fn fold[T, U](self : Array[T], f : (T, U) -> U, ~init : U) -> U { for i = 0, acc = init; i < self.length(); { continue i + 1, f(self[i], acc) @@ -237,6 +277,13 @@ test "fold" { @assertion.assert_eq(sum, 15)? } +/// Fold out values from an array according to certain rules in reversed turn. +/// +/// # Example +/// ``` +/// let sum = [1, 2, 3, 4, 5].fold_rev(~init=0, fn { sum, elem => sum + elem }) +/// sum // 15 +/// ``` pub fn fold_rev[T, U](self : Array[T], f : (T, U) -> U, ~init : U) -> U { for i = self.length() - 1, acc = init; i >= 0; { continue i - 1, f(self[i], acc) @@ -250,7 +297,13 @@ test "fold_rev" { @assertion.assert_eq(sum, 15)? } -/// Reverse the array in place. +/// Fold out values from an array according to certain rules in reversed turn. +/// +/// # Example +/// ``` +/// let arr = [1, 2, 3, 4, 5] +/// reverse(arr) // [5, 4, 3, 2, 1] +/// ``` pub fn reverse[T](self : Array[T]) -> Unit { let mid_len = self.length() / 2 for i = 0; i < mid_len; i = i + 1 { @@ -289,3 +342,175 @@ pub fn swap[T](self : Array[T], i : Int, j : Int) -> Unit { self[i] = self[j] self[j] = temp } + +test "swap" { + let arr = [1, 2, 3, 4, 5] + arr.swap(0, 1) + @assertion.assert_eq(arr, [2, 1, 3, 4, 5])? +} + +/// Check if all the elements in the array match the condition. +/// +/// # Example +/// +/// ``` +/// let arr = [1, 2, 3, 4, 5] +/// arr.all(fn(ele) { ele < 6 }) // true +/// arr.all(fn(ele) { ele < 5 }) // false +/// ``` +pub fn all[T](self : Array[T], f : (T) -> Bool) -> Bool { + for i = 0; i < self.length(); i = i + 1 { + if f(self[i]).not() { + return false + } + } + true +} + +test "all" { + let arr = [1, 2, 3, 4, 5] + @assertion.assert_eq(arr.all(fn(ele) { ele < 6 }), true)? + @assertion.assert_eq(arr.all(fn(ele) { ele < 5 }), false)? +} + +/// Check if any of the elements in the array match the condition. +/// +/// # Example +/// +/// ``` +/// let arr = [1, 2, 3, 4, 5] +/// arr.any(fn(ele) { ele < 6 }) // true +/// arr.any(fn(ele) { ele < 5 }) // true +/// ``` +pub fn any[T](self : Array[T], f : (T) -> Bool) -> Bool { + for i = 0; i < self.length(); i = i + 1 { + if f(self[i]) { + return true + } + } + false +} + +test "any" { + let arr = [1, 2, 3, 4, 5] + @assertion.assert_eq(arr.any(fn(ele) { ele < 6 }), true)? + @assertion.assert_eq(arr.any(fn(ele) { ele < 5 }), true)? +} + +/// Fill the array with a given value. +/// +/// # Example +/// ``` +/// [0, 0, 0, 0, 0].fill(3) // [3, 3, 3, 3, 3] +/// ``` +pub fn fill[T](self : Array[T], value : T) -> Unit { + for i = 0; i < self.length(); i = i + 1 { + self[i] = value + } +} + +test "fill" { + let arr = [0, 0, 0, 0, 0] + arr.fill(3) + @assertion.assert_eq(arr, [3, 3, 3, 3, 3])? +} + +/// Search the array index for a given element. +/// +/// # Example +/// ``` +/// let arr = [3, 4, 5] +/// arr.search(3) // 0 +/// ``` +pub fn search[T : Eq](self : Array[T], value : T) -> Option[Int] { + for i = 0; i < self.length(); i = i + 1 { + if self[i] == value { + return Some(i) + } + } + None +} + +test "search" { + let arr = [1, 2, 3] + @assertion.assert_eq(arr.search(3), Some(2))? + @assertion.assert_eq(arr.search(-1), None)? +} + +/// Checks if the array contains an element. +/// +/// # Example +/// ``` +/// let arr = [3, 4, 5] +/// arr.contains(3) +/// ``` +pub fn contains[T : Eq](self : Array[T], value : T) -> Bool { + for i = 0; i < self.length(); i = i + 1 { + if self[i] == value { + return true + } + } + false +} + +test "contains" { + let arr = [3, 4, 5] + @assertion.assert_true(arr.contains(3))? + @assertion.assert_true(arr.contains(4))? + @assertion.assert_true(arr.contains(5))? + @assertion.assert_false(arr.contains(6))? +} + +/// Check if the array starts with a given prefix. +/// +/// # Example +/// ``` +/// let arr = [3, 4, 5] +/// arr.starts_with([3, 4]) // true +/// ``` +pub fn starts_with[T : Eq](self : Array[T], prefix : Array[T]) -> Bool { + if prefix.length() > self.length() { + return false + } + for i = 0; i < prefix.length(); i = i + 1 { + if self[i] != prefix[i] { + return false + } + } + true +} + +test "starts_with" { + let arr = [3, 4, 5] + @assertion.assert_true(arr.starts_with([3, 4]))? + @assertion.assert_true(arr.starts_with([3, 4, 5]))? + @assertion.assert_false(arr.starts_with([3, 4, 5, 6]))? +} + +/// Check if the array ends with a given suffix. +/// +/// # Example +/// ``` +/// let v = [3, 4, 5] +/// v.ends_with([5]) // true +/// ``` +pub fn ends_with[T : Eq](self : Array[T], suffix : Array[T]) -> Bool { + let self_len = self.length() + let suf_len = suffix.length() + if suf_len > self_len { + return false + } + for i = 0; i < suf_len; i = i + 1 { + if self[self_len - suf_len + i] != suffix[i] { + return false + } + } + true +} + +test "ends_with" { + let arr = [3, 4, 5] + @assertion.assert_true(arr.ends_with([4, 5]))? + @assertion.assert_true(arr.ends_with([3, 4, 5]))? + @assertion.assert_false(arr.ends_with([2, 3, 4, 5]))? +} diff --git a/deque/README.md b/deque/README.md index 769baf0bf..612c99f4a 100644 --- a/deque/README.md +++ b/deque/README.md @@ -90,6 +90,17 @@ dq.front() // Some(2) dq.length() // 3 ``` +If you only want to pop an element without getting the return value, you can use `pop_front_exn()` with `pop_back_exn()`. +These two functions will panic if the queue is empty. + +```moonbit +let dq = Deque::[1, 2, 3, 4, 5] +dq.pop_front_exn() +dq.front() // Some(2) +dq.pop_back_exn() +dq.back() // Some(3) +``` + ### Clear You can use `clear` to clear a deque. But note that the memory it already occupies does not change. @@ -122,12 +133,15 @@ deque supports vector-like `iter/iteri/map/mapi` functions and their inverse for dq.mapi(fn(i, elem) { elem + i }) ``` -### TODO List +### Search & Contains + +You can use `contains()` to find out if a value is in the deque, or `search()` to find its index in the deque. + +```moonbit +let dq = Deque::[1, 2, 3, 4, 5] +dq.contains(1) // true +dq.contains(6) // false +dq.search(1) // Some(0) +dq.search(6) // None +``` -- [ ] fold/foldr -- [ ] reverse -- [ ] contains -- [ ] search -- [ ] swap -- [ ] resize -- [ ] fill diff --git a/deque/deque.mbt b/deque/deque.mbt index 578125bbd..44b0fdb3c 100644 --- a/deque/deque.mbt +++ b/deque/deque.mbt @@ -164,6 +164,38 @@ pub fn push_back[T](self : Deque[T], value : T) -> Unit { self.len += 1 } +/// Removes a fornt element from a deque. +/// +/// Panics if the deque is empty. +/// # Example +/// ``` +/// let dq = Deque::[1, 2, 3, 4, 5] +/// dq.pop_front_exn() +/// ``` +pub fn pop_front_exn[T](self : Deque[T]) -> Unit { + if self.len == 0 { + abort("The deque is empty!") + } + self.head = if self.head < self.buf.length() - 1 { self.head + 1 } else { 0 } + self.len -= 1 +} + +/// Removes a back element from a deque. +/// +/// Panics if the deque is empty. +/// # Example +/// ``` +/// let dq = Deque::[1, 2, 3, 4, 5] +/// dq.pop_back_exn() +/// ``` +pub fn pop_back_exn[T](self : Deque[T]) -> Unit { + if self.len == 0 { + abort("The deque is empty!") + } + self.tail = if self.tail > 0 { self.tail - 1 } else { self.buf.length() - 1 } + self.len -= 1 +} + /// Removes a fornt element from a deque and returns it, or `None` if it is empty. /// /// # Example @@ -228,9 +260,11 @@ test "push_and_pop" { /// println(dq[2]) // 3 /// ``` pub fn op_get[T](self : Deque[T], index : Int) -> T { - if index >= self.len { + if index < 0 || index >= self.len { let len = self.len - abort("index out of bounds: the len is \(len) but the index is \(index)") + abort( + "index out of bounds: the len is from 0 to \(len) but the index is \(index)", + ) } if self.head + index < self.buf.length() { self.buf[self.head + index] @@ -263,9 +297,11 @@ test "op_get" { /// println(dq[2]) // 1 /// ``` pub fn op_set[T](self : Deque[T], index : Int, value : T) -> Unit { - if index >= self.len { + if index < 0 || index >= self.len { let len = self.len - abort("index out of bounds: the len is \(len) but the index is \(index)") + abort( + "index out of bounds: the len is from 0 to \(len) but the index is \(index)", + ) } if self.head + index < self.buf.length() { self.buf[self.head + index] = value @@ -317,8 +353,8 @@ test "iter" { test "iter2" { let v = Deque::[1, 2, 3, 4, 5] - v.pop_front() |> ignore - v.pop_back() |> ignore + v.pop_front_exn() + v.pop_back_exn() let mut sum = 0 v.iter(fn { x => sum += x }) @assertion.assert_eq(sum, 9)? @@ -489,3 +525,51 @@ test "is_empty" { dq.push_back(3) @assertion.assert_false(dq.is_empty())? } + +/// Search the deque index for a given element. +/// +/// # Example +/// ``` +/// let dq = Deque::[3, 4, 5] +/// dq.search(3) // Some(0) +/// ``` +pub fn search[T : Eq](self : Deque[T], value : T) -> Option[Int] { + for i = 0; i < self.len; i = i + 1 { + if self.buf[i] == value { + return Some(i) + } + } + None +} + +test "search" { + let dq = Deque::[3, 4, 5] + @assertion.assert_eq(dq.search(3), Some(0))? + @assertion.assert_eq(dq.search(4), Some(1))? + @assertion.assert_eq(dq.search(5), Some(2))? + @assertion.assert_eq(dq.search(6), None)? +} + +/// Checks if the vector contains an element. +/// +/// # Example +/// ``` +/// let dq = Deque::[3, 4, 5] +/// dq.contains(3) // true +/// ``` +pub fn contains[T : Eq](self : Deque[T], value : T) -> Bool { + for i = 0; i < self.len; i = i + 1 { + if self.buf[i] == value { + return true + } + } + false +} + +test "contains" { + let dq = Deque::[3, 4, 5] + @assertion.assert_true(dq.contains(3))? + @assertion.assert_true(dq.contains(4))? + @assertion.assert_true(dq.contains(5))? + @assertion.assert_false(dq.contains(6))? +} diff --git a/list/README.md b/list/README.md new file mode 100644 index 000000000..ab7151414 --- /dev/null +++ b/list/README.md @@ -0,0 +1,133 @@ +# Moonbit/Core List + +## Overview + +List is implemented as a **linked list**, supporting O(1) head access. +- Moonbit list is **homogeneous** list, which means all elements in the list must be of the same type. +- Moonbit list does not support random access well, you can only access elements by iterating through the list. If you need randomly access the nth element, you should use `Vec` instead. + +## Usage + +### Building lists +You can create a list manually via the `new()` or construct it using the `from_array()`. +```moonbit +let list0 : List[Int] = List::new() +let list1 = List::[1, 2, 3, 4, 5] +let list2 = List::from_array([1, 2, 3, 4, 5]) +``` + +Or use `Cons` constructor directly (Adds a single element to the beginning of a list): +```moonbit +let list = Cons(1, Cons(2, Cons(3, Nil))) +``` + +Build a repeated list by using the `repeat()` method: +```moonbit +let list = repeat(3, 1) // List::[1, 1, 1] +``` + +### Pattern matching +You can use pattern matching to destructure a list: +```moonbit +let list = List::[1, 2, 3, 4, 5] +match list { + Cons(head, tail) => print(head) + Nil => print("Empty list") +} +``` + +### Iterating over a list +The standard library provides a lot of tools for iterating over a list, such as `iter()`, `iteri()`, etc. (For details check the API documentation) +```moonbit +let list = List::[1, 2, 3, 4, 5] +let list1 = list.iter(fn (ele) { print(ele) }) +``` + +### Appending / Joining lists +To simply concatenate two lists, you can use the `concat()` method (or `+` operator): +```moonbit +let list1 = List::[1, 2, 3] +let list2 = List::[4, 5, 6] +let list3 = list1.concat(list2) // List::[1, 2, 3, 4, 5, 6] +let list4 = list1 + list2 // List::[1, 2, 3, 4, 5, 6] +``` + +For concatenating multiple lists (especially the length is unknown), you can use the `flatten()` method: +```moonbit +let list1 = List::[1, 2, 3] +let list2 = List::[4, 5, 6] +let list3 = List::[7, 8, 9] +let list4 = List::flatten([list1, list2, list3]) // List::[1, 2, 3, 4, 5, 6, 7, 8, 9] +``` + +To concatenate a list with a delimiter, `intercalate()` is useful: +```moonbit +let list = List::[List::[1, 2, 3], List::[4, 5, 6], List::[7, 8, 9]] +let list1 = list.intercalate(List::[0]) // List::[1, 2, 3, 0, 4, 5, 6, 0, 7, 8, 9] +``` + +### Filtering / Rejecting / Selecting elements +There are three ways to filter / reject / select multiple elements from a list: +- Go through the entire list and decide whether the element should be present in the resultant list or not. Use `filter` for this. +- To extract the first (or last) N elements of a list (and N is independent of the contents of the list). Use `take` or `drop` in this case. +- To stop selecting elements (terminate the iteration) as soon as a condition is met, using `take_while` or `drop_while` + +```moonbit +let ls = List::[1, 2, 3, 4, 5] +ls.filter(fn (ele) { ele % 2 == 0 }) // List::[2, 4] +ls.take(2) // List::[1, 2] +ls.drop(2) // List::[3, 4, 5] +ls.take_while(fn (ele) { ele < 3 }) // List::[1, 2] +ls.drop_while(fn (ele) { ele < 3 }) // List::[3, 4, 5] +``` + +### Accessing elements / sub-lists +You can access the head of the list using the `head()` (O(1)) method, and the last element in list using the `last()` (O(n)) method. +These two functions will panic if the list is empty. +```moonbit +let list = List::[1, 2, 3, 4, 5] +list.head() // 1 +list.last() // 5 +``` + +For randomly accessing, you can use the `nth()` method, which returns the nth element in the list (O(n)). +This method will panic if the index is out of bounds. For a safe alternative, use `nth_option()` method. +```moonbit +let list = List::[1, 2, 3, 4, 5] +list.nth(2) // 3 +list.nth_option(2) // Some(3) +``` + +To get a sub-list from the list, you can use the `init_()` method for getting all elements except the last one, and `tail()` for getting all elements except the first one. +```moonbit +let list = List::[1, 2, 3, 4, 5] +list.init_() // List::[1, 2, 3, 4] +list.tail() // List::[2, 3, 4, 5] +``` + +### Reducing Lists +You can reduce (fold) a list to a single value using the `fold()` method. +```moonbit +let list = List::[1, 2, 3, 4, 5] +list.fold(0, fn(acc, x) { acc + x }) // 15 +``` + +#### Special folds +- `any` returns true if any element in the list satisfies the predicate. +- `all` returns true if all elements in the list satisfy the predicate. +- `sum` returns the sum of all elements in the list. +- `maximum` returns the maximum element in the list. +- `minimum` returns the minimum element in the list. + +### List transformations +To transform list elements, you can use the `map()` method. +```moonbit +let list = List::[1, 2, 3, 4, 5] +list.map(fn (ele) { ele * 2 }) // List::[2, 4, 6, 8, 10] +``` + +The `reverse` method reverses the list. +```moonbit +let list = List::[1, 2, 3, 4, 5] +list.reverse() // List::[5, 4, 3, 2, 1] +``` \ No newline at end of file diff --git a/map/map.mbt b/map/map.mbt index f8f745628..6682c9dc9 100644 --- a/map/map.mbt +++ b/map/map.mbt @@ -47,7 +47,7 @@ pub fn singleton[K, V](key : K, value : V) -> Map[K, V] { /// Check if the map contains a key. /// O(log n). -pub fn member[K : Compare, V : Show](self : Map[K, V], key : K) -> Bool { +pub fn member[K : Compare, V](self : Map[K, V], key : K) -> Bool { loop self { Empty => false Tree(k, _, _, l, r) => { diff --git a/queue/README.md b/queue/README.md new file mode 100644 index 000000000..45f7d6879 --- /dev/null +++ b/queue/README.md @@ -0,0 +1,61 @@ +# Moonbit/Core Queue +## Overview +Queue is a first in first out (FIFO) data structure, allowing to process their elements in the order they come. + +## Usage +### Create and Clear +You can create a queue manually by using the `new` or construct it using the `from_array`. +```moonbit +let queue = Queue::new() +let queue2 = Queue::[1,2,3] +let queue3 = Queue::[1,1,4] +``` + +To clear the queue, you can use the `clear` method. +```moonbit +let queue = Queue::[1,2,3] +queue.clear() +``` + +### Length +You can get the length of the queue by using the `length` method. The `is_empty` method can be used to check if the queue is empty. +```moonbit +let queue = Queue::[1,2,3] +queue.length() // 3 +queue.is_empty() // false +``` + +### Pop and Push +You can add elements to the queue using the `push` method and remove them using the `pop` method. +The unsafe version of `pop` is `pop_exn`, which will panic if the queue is empty. +```moonbit +let queue = Queue::new() +queue.push(1) +queue.push(2) +queue.push(3) +queue.pop_exn() // 1 +queue.pop() // Some(2) +queue.pop() // Some(3) +``` + +### Peek +You can get the first element of the queue without removing it using the `peek` method. +```moonbit +let queue = Queue::[1,2,3] +queue.peek() // Some(1) +``` +The unsafe version of the `peek` method is the `peek_exn` method, which will panic if the queue is empty. + +### Traverse +You can traverse the queue using the `iter` method. +```moonbit +let queue = Queue::[1,2,3] +let mut sum = 0 +queue.iter(fn(x) { sum += x }) // sum = 6 +``` + +You can fold the queue using the `fold` method. +```moonbit +let queue = Queue::[1,2,3] +let sum = queue.fold(~init = 0, fn(acc, x) { acc + x }) // sum = 6 +``` \ No newline at end of file diff --git a/queue/moon.pkg.json b/queue/moon.pkg.json new file mode 100644 index 000000000..dc09758b7 --- /dev/null +++ b/queue/moon.pkg.json @@ -0,0 +1,5 @@ +{ + "import": [ + "moonbitlang/core/assertion" + ] +} \ No newline at end of file diff --git a/queue/queue.mbt b/queue/queue.mbt new file mode 100644 index 000000000..68f64c2c6 --- /dev/null +++ b/queue/queue.mbt @@ -0,0 +1,334 @@ +// Copyright 2024 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +struct Cont[T] { + content : T + mut next : Cell[T] +} + +enum Cell[T] { + Nil + Cons(Cont[T]) +} + +struct Queue[T] { + mut length : Int + mut first : Cell[T] + mut last : Cell[T] +} + +/// Creates a new empty queue. +/// +/// # Example +/// ``` +/// let queue : Queue[Int] = Queue::new() +/// ``` +pub fn Queue::new[T]() -> Queue[T] { + { length: 0, first: Nil, last: Nil } +} + +test "new" { + let queue : Queue[Int] = new() + @assertion.assert_eq(queue.length, 0)? +} + +/// Creates a new queue from an array. +/// +/// # Example +/// ``` +/// let queue : Queue[Int] = Queue::from_array([1, 2, 3, 4]) +/// ``` +pub fn Queue::from_array[T](arr : Array[T]) -> Queue[T] { + if arr.length() == 0 { + return new() + } + let queue : Queue[T] = Queue::new() + queue.length = arr.length() + for i = arr.length(); i > 0; i = i - 1 { + let cell : Cell[T] = Cons({ content: arr[i - 1], next: queue.first }) + queue.first = cell + } + queue.last = queue.first + queue +} + +/// Tests if two queue cells are equal. +pub fn op_equal[T : Eq](self : Cell[T], other : Cell[T]) -> Bool { + loop self, other { + Nil, Nil => true + Cons({ content: x, next: xs }), Cons({ content: y, next: ys }) => { + if x != y { + break false + } + continue xs, ys + } + _, _ => false + } +} + +test "from_array" { + let queue : Queue[Int] = Queue::[1, 2, 3, 4] + let queue2 : Queue[Int] = Queue::[1] + @assertion.assert_eq(queue.length, 4)? + @assertion.assert_eq(queue.pop_exn(), 1)? + @assertion.assert_eq(queue.pop_exn(), 2)? + @assertion.assert_eq(queue.pop_exn(), 3)? + @assertion.assert_eq(queue.pop_exn(), 4)? + @assertion.assert_eq(queue2.pop_exn(), 1)? + @assertion.assert_eq(queue2.length, 0)? +} + +/// Clears the queue. +/// +/// # Example +/// ``` +/// let queue : Queue[Int] = Queue::[1, 2, 3, 4] +/// queue.clear() +/// ``` +pub fn clear[T](self : Queue[T]) -> Unit { + self.length = 0 + self.first = Nil + self.last = Nil +} + +test "clear" { + let queue : Queue[Int] = Queue::[1, 2, 3, 4] + @assertion.assert_eq(queue.length, 4)? + queue.clear() + @assertion.assert_eq(queue.length, 0)? +} + +pub fn length[T](self : Queue[T]) -> Int { + self.length +} + +/// Checks if the queue is empty. +/// +/// # Example +/// ``` +/// let queue : Queue[Int] = Queue::new() +/// let is_empty = queue.is_empty() +/// ``` +pub fn is_empty[T](self : Queue[T]) -> Bool { + self.length == 0 +} + +test "is_empty" { + let queue : Queue[Int] = new() + @assertion.assert_true(queue.is_empty())? + queue.push(1) + queue.push(2) + queue.push(3) + queue.push(4) + @assertion.assert_false(queue.is_empty())? +} + +/// Adds a value to the queue. +/// +/// # Example +/// ``` +/// let queue : Queue[Int] = Queue::new() +/// queue.add(1) +/// ``` +pub fn push[T](self : Queue[T], x : T) -> Unit { + let cell : Cell[T] = Cons({ content: x, next: Nil }) + match self.last { + Nil => { + self.length = 1 + self.first = cell + self.last = cell + } + Cons(last) => { + last.next = cell + self.length = self.length + 1 + self.last = cell + } + } +} + +test "push" { + let queue : Queue[Int] = Queue::new() + queue.push(1) + queue.push(2) + queue.push(3) + queue.push(1) + @assertion.assert_eq(queue.length, 4)? + @assertion.assert_eq(queue.pop_exn(), 1)? + @assertion.assert_eq(queue.pop_exn(), 2)? + @assertion.assert_eq(queue.pop_exn(), 3)? + @assertion.assert_eq(queue.pop_exn(), 1)? +} + +/// Peeks at the first value in the queue. +/// +/// Panics if the queue is empty. +/// # Example +/// ``` +/// let queue : Queue[Int] = Queue::[1, 2, 3, 4] +/// let first = queue.peek_exn() +/// ``` +pub fn peek_exn[T](self : Queue[T]) -> T { + match self.first { + Nil => abort("Queue is empty") + Cons(first) => first.content + } +} + +test "peek_exn" { + let queue : Queue[Int] = Queue::[1, 2, 3, 4] + @assertion.assert_eq(queue.peek_exn(), 1)? + @assertion.assert_eq(queue.length, 4)? + @assertion.assert_eq(queue.pop_exn(), 1)? + @assertion.assert_eq(queue.peek_exn(), 2)? + @assertion.assert_eq(queue.length, 3)? +} + +/// Peeks at the first value in the queue, which returns None if the queue is empty. +/// +/// # Example +/// ``` +/// let queue : Queue[Int] = Queue::[1, 2, 3, 4] +/// let first = queue.peek() +/// ``` +pub fn peek[T](self : Queue[T]) -> Option[T] { + match self.first { + Nil => None + Cons(first) => Some(first.content) + } +} + +test "peek" { + let queue : Queue[Int] = Queue::[1, 2, 3, 4] + @assertion.assert_eq(queue.peek(), Some(1))? + queue.clear() + @assertion.assert_eq(queue.peek(), None)? +} + +/// Pops the first value from the queue. +/// +/// Panics if the queue is empty. +/// # Example +/// ``` +/// let queue : Queue[Int] = Queue::[1, 2, 3, 4] +/// let first = queue.pop_exn() +/// ``` +pub fn pop_exn[T](self : Queue[T]) -> T { + match self.first { + Nil => abort("Queue is empty") + Cons({ content, next: Nil }) => { + self.clear() + content + } + Cons({ content, next }) => { + self.length = self.length - 1 + self.first = next + content + } + } +} + +test "pop_exn" { + let queue : Queue[Int] = Queue::[1, 2, 3, 4] + @assertion.assert_eq(queue.pop_exn(), 1)? + @assertion.assert_eq(queue.pop_exn(), 2)? + @assertion.assert_eq(queue.pop_exn(), 3)? + @assertion.assert_eq(queue.pop_exn(), 4)? + @assertion.assert_eq(queue.length, 0)? +} + +/// Pops the first value from the queue, which returns None if the queue is empty. +/// +/// # Example +/// ``` +/// let queue : Queue[Int] = Queue::[1, 2, 3, 4] +/// let first = queue.pop() +/// ``` +pub fn pop[T](self : Queue[T]) -> Option[T] { + if self.length == 0 { + None + } else { + Some(self.pop_exn()) + } +} + +test "pop" { + let queue : Queue[Int] = Queue::[1, 2, 3, 4] + @assertion.assert_eq(queue.pop(), Some(1))? + @assertion.assert_eq(queue.pop(), Some(2))? + @assertion.assert_eq(queue.pop(), Some(3))? + @assertion.assert_eq(queue.pop(), Some(4))? + @assertion.assert_eq(queue.pop(), None)? +} + +/// Iterates over the queue. +/// +/// # Example +/// ``` +/// let queue : Queue[Int] = Queue::[1, 2, 3, 4] +/// queue.iter(fn(x) { print(x) }) +/// ``` +pub fn iter[T](self : Queue[T], f : (T) -> Unit) -> Unit { + loop self.first { + Cons({ content, next }) => { + f(content) + continue next + } + Nil => () + } +} + +test "iter" { + let queue : Queue[Int] = new() + let mut sum = 0 + queue.iter(fn(x) { sum = sum + x }) + @assertion.assert_eq(sum, 0)? + queue.push(1) + queue.push(2) + queue.push(3) + queue.push(4) + sum = 0 + queue.iter(fn(x) { sum = sum + x }) + @assertion.assert_eq(sum, 10)? +} + +/// Folds over the queue. +/// +/// # Example +/// ``` +/// let queue : Queue[Int] = Queue::[1, 2, 3, 4] +/// let sum = queue.fold(0, fn(acc, x) { acc + x }) +/// ``` +pub fn fold[T](self : Queue[T], ~init : T, f : (T, T) -> T) -> T { + loop self.first, init { + cell, acc => + match cell { + Nil => acc + Cons({ content, next }) => continue next, f(acc, content) + } + } +} + +test "fold" { + let queue : Queue[Int] = new() + let sum = queue.fold(~init=0, fn(acc, x) { acc + x }) + @assertion.assert_eq(sum, 0)? + queue.push(1) + queue.push(2) + queue.push(3) + queue.push(4) + let sum = queue.fold(~init=0, fn(acc, x) { acc + x }) + @assertion.assert_eq(sum, 10)? + let sum2 = Queue::[1, 2, 3, 4].fold(~init=0, fn(acc, x) { acc + x }) + @assertion.assert_eq(sum2, 10)? +} diff --git a/vec/vec.mbt b/vec/vec.mbt index 72c94bde6..6618b7af0 100644 --- a/vec/vec.mbt +++ b/vec/vec.mbt @@ -875,6 +875,13 @@ test "join" { @assertion.assert_eq(vv.length(), 5)? } +/// Fold out values from an vector according to certain rules. +/// +/// # Example +/// ``` +/// let sum = Vec::[1, 2, 3, 4, 5].fold(~init=0, fn(elem, sum) { sum + elem }) +/// sum // 15 +/// ``` pub fn fold[T, U](self : Vec[T], f : (T, U) -> U, ~init : U) -> U { for i = 0, acc = init; i < self.length(); { continue i + 1, f(self[i], acc) @@ -888,6 +895,13 @@ test "fold" { @assertion.assert_eq(sum, 15)? } +/// Fold out values from an vector according to certain rules in reversed turn. +/// +/// # Example +/// ``` +/// let sum = Vec::[1, 2, 3, 4, 5].fold_rev(~init=0, fn(elem, sum) { sum + elem }) +/// sum // 15 +/// ``` pub fn fold_rev[T, U](self : Vec[T], f : (T, U) -> U, ~init : U) -> U { for i = self.length() - 1, acc = init; i >= 0; { continue i - 1, f(self[i], acc)