From 447ce90e5b3a69f95151dcd7bfb5348aacc42994 Mon Sep 17 00:00:00 2001 From: CAIMEOX <1853884864@qq.com> Date: Thu, 21 Mar 2024 11:54:14 +0800 Subject: [PATCH 01/23] docs: list document basic setup --- list/README.md | 133 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 list/README.md diff --git a/list/README.md b/list/README.md new file mode 100644 index 000000000..1ae0c4ea1 --- /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 **homogenous** 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] +``` + +### Patten 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 From 6165200a87ff1cba94b376d235b6765aa54b0b42 Mon Sep 17 00:00:00 2001 From: CAIMEOX <1853884864@qq.com> Date: Thu, 21 Mar 2024 11:58:15 +0800 Subject: [PATCH 02/23] fix typo --- list/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/list/README.md b/list/README.md index 1ae0c4ea1..ab7151414 100644 --- a/list/README.md +++ b/list/README.md @@ -3,7 +3,7 @@ ## Overview List is implemented as a **linked list**, supporting O(1) head access. -- Moonbit list is **homogenous** list, which means all elements in the list must be of the same type. +- 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 @@ -26,7 +26,7 @@ Build a repeated list by using the `repeat()` method: let list = repeat(3, 1) // List::[1, 1, 1] ``` -### Patten matching +### Pattern matching You can use pattern matching to destructure a list: ```moonbit let list = List::[1, 2, 3, 4, 5] From 77a05a83b30dbebc914394b9288aa8e01f17f7b0 Mon Sep 17 00:00:00 2001 From: CAIMEOX <1853884864@qq.com> Date: Sat, 16 Mar 2024 13:24:44 +0800 Subject: [PATCH 03/23] queue basic setup --- queue/moon.pkg.json | 5 ++ queue/queue.mbt | 115 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 queue/moon.pkg.json create mode 100644 queue/queue.mbt 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..04f4578fd --- /dev/null +++ b/queue/queue.mbt @@ -0,0 +1,115 @@ +struct Cell[T] { + content : T + mut next : Option[Cell[T]] +} + +pub fn op_equal[T : Eq](self : Cell[T], other : Cell[T]) -> Bool { + self.content == other.content +} + +pub struct Queue[T] { + mut length : Int + mut tail : Option[Cell[T]] +} + +pub fn create[T]() -> Queue[T] { + { length: 0, tail: None } +} + +pub fn clear[T](self : Queue[T]) { + self.length = 0 + self.tail = None +} + +pub fn add[T](self : Queue[T], value : T) { + if self.length == 0 { + let cell : Cell[T] = { content: value, next: None } + cell.next = Some(cell) + self.length = 1 + self.tail = Some(cell) + } else { + let tail = self.tail.unwrap() + let head = tail.next + let cell = { content: value, next: head } + self.length = self.length + 1 + tail.next = Some(cell) + self.tail = Some(cell) + } +} + +pub fn peek[T](self : Queue[T]) -> Option[T] { + let tail = self.tail + match tail { + Some(tail) => { + let head = tail.next + match head { + Some(head) => Some(head.content) + None => None + } + } + None => None + } +} + +pub fn length[T](self : Queue[T]) -> Int { + self.length +} + +pub fn is_empty[T](self : Queue[T]) -> Bool { + self.length == 0 +} + +pub fn take[T : Eq](self : Queue[T]) -> T { + if self.length == 0 { + abort("Queue is empty") + } + self.length = self.length - 1 + let tail = self.tail.unwrap() + let head = tail.next.unwrap() + if head == tail { + self.tail = None + } else { + tail.next = head.next + } + head.content +} + +pub fn take_option[T : Eq](self : Queue[T]) -> Option[T] { + if self.length == 0 { + None + } else { + Some(self.take()) + } +} + +pub fn iter[T : Eq](self : Queue[T], f : (T) -> Unit) { + if self.length > 0 { + let tail = self.tail.unwrap() + fn helper(cell : Cell[T]) { + f(cell.content) + if (cell == tail).not() { + helper(cell.next.unwrap()) + } + } + + helper(tail.next.unwrap()) + } +} + +pub fn fold[T : Eq](self : Queue[T], init : T, f : (T, T) -> T) -> T { + if self.length == 0 { + init + } else { + let tail = self.tail.unwrap() + fn helper(cell : Cell[T], acc : T) -> T { + let new_acc = f(acc, cell.content) + if cell == tail { + new_acc + } else { + helper(cell.next.unwrap(), new_acc) + } + } + helper(tail.next.unwrap(), init) + } +} + From 888c7ab40da3bbc675a9ec99b6aba1c8d4d4b038 Mon Sep 17 00:00:00 2001 From: CAIMEOX <1853884864@qq.com> Date: Sat, 16 Mar 2024 13:39:09 +0800 Subject: [PATCH 04/23] add test cases --- queue/queue.mbt | 183 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 169 insertions(+), 14 deletions(-) diff --git a/queue/queue.mbt b/queue/queue.mbt index 04f4578fd..71f4f9934 100644 --- a/queue/queue.mbt +++ b/queue/queue.mbt @@ -12,15 +12,73 @@ pub struct Queue[T] { mut tail : Option[Cell[T]] } -pub fn create[T]() -> Queue[T] { +pub fn Queue::create[T]() -> Queue[T] { { length: 0, tail: None } } +test "create" { + let queue : Queue[Int] = create() + @assertion.assert_eq(queue.length, 0)? + @assertion.assert_true(queue.tail == None)? +} + +pub fn Queue::from_array[T](array : Array[T]) -> Queue[T] { + let queue : Queue[T] = create() + for i = 0; i < array.length(); i = i + 1 { + queue.add(array[i]) + } + queue +} + +test "from_array" { + let queue : Queue[Int] = Queue::[1, 2, 3, 4] + @assertion.assert_eq(queue.length, 4)? + @assertion.assert_eq(queue.take(), 1)? + @assertion.assert_eq(queue.take(), 2)? + @assertion.assert_eq(queue.take(), 3)? + @assertion.assert_eq(queue.take(), 4)? + @assertion.assert_eq(queue.length, 0)? +} + +pub fn Queue::from_list[T](list : List[T]) -> Queue[T] { + let queue : Queue[T] = create() + loop list { + l => + match l { + Cons(head, tail) => { + queue.add(head) + continue tail + } + Nil => break + } + } + queue +} + +test "from_list" { + let queue : Queue[Int] = Queue::from_list( + Cons(1, Cons(2, Cons(3, Cons(4, Nil)))), + ) + @assertion.assert_eq(queue.length, 4)? + @assertion.assert_eq(queue.take(), 1)? + @assertion.assert_eq(queue.take(), 2)? + @assertion.assert_eq(queue.take(), 3)? + @assertion.assert_eq(queue.take(), 4)? + @assertion.assert_eq(queue.length, 0)? +} + pub fn clear[T](self : Queue[T]) { self.length = 0 self.tail = None } +test "clear" { + let queue : Queue[Int] = Queue::[1, 2, 3, 4] + queue.clear() + @assertion.assert_eq(queue.length, 0)? + @assertion.assert_true(queue.tail == None)? +} + pub fn add[T](self : Queue[T], value : T) { if self.length == 0 { let cell : Cell[T] = { content: value, next: None } @@ -37,6 +95,20 @@ pub fn add[T](self : Queue[T], value : T) { } } +test "add" { + let queue : Queue[Int] = create() + queue.add(1) + queue.add(2) + queue.add(3) + queue.add(4) + @assertion.assert_eq(queue.length, 4)? + @assertion.assert_eq(queue.take(), 1)? + @assertion.assert_eq(queue.take(), 2)? + @assertion.assert_eq(queue.take(), 3)? + @assertion.assert_eq(queue.take(), 4)? + @assertion.assert_eq(queue.length, 0)? +} + pub fn peek[T](self : Queue[T]) -> Option[T] { let tail = self.tail match tail { @@ -51,14 +123,52 @@ pub fn peek[T](self : Queue[T]) -> Option[T] { } } +test "peek" { + let queue : Queue[Int] = create() + @assertion.assert_eq(queue.peek(), None)? + queue.add(1) + queue.add(2) + queue.add(3) + queue.add(4) + @assertion.assert_eq(queue.peek(), Some(1))? + @assertion.assert_eq(queue.take(), 1)? + @assertion.assert_eq(queue.peek(), Some(2))? + @assertion.assert_eq(queue.take(), 2)? + @assertion.assert_eq(queue.peek(), Some(3))? + @assertion.assert_eq(queue.take(), 3)? + @assertion.assert_eq(queue.peek(), Some(4))? + @assertion.assert_eq(queue.take(), 4)? + @assertion.assert_eq(queue.peek(), None)? +} + pub fn length[T](self : Queue[T]) -> Int { self.length } +test "length" { + let queue : Queue[Int] = create() + @assertion.assert_eq(queue.length(), 0)? + queue.add(1) + queue.add(2) + queue.add(3) + queue.add(4) + @assertion.assert_eq(queue.length(), 4)? +} + pub fn is_empty[T](self : Queue[T]) -> Bool { self.length == 0 } +test "is_empty" { + let queue : Queue[Int] = create() + @assertion.assert_true(queue.is_empty())? + queue.add(1) + queue.add(2) + queue.add(3) + queue.add(4) + @assertion.assert_false(queue.is_empty())? +} + pub fn take[T : Eq](self : Queue[T]) -> T { if self.length == 0 { abort("Queue is empty") @@ -74,6 +184,15 @@ pub fn take[T : Eq](self : Queue[T]) -> T { head.content } +test "take" { + let queue : Queue[Int] = Queue::[1, 2, 3, 4] + @assertion.assert_eq(queue.take(), 1)? + @assertion.assert_eq(queue.take(), 2)? + @assertion.assert_eq(queue.take(), 3)? + @assertion.assert_eq(queue.take(), 4)? + @assertion.assert_eq(queue.length(), 0)? +} + pub fn take_option[T : Eq](self : Queue[T]) -> Option[T] { if self.length == 0 { None @@ -82,34 +201,70 @@ pub fn take_option[T : Eq](self : Queue[T]) -> Option[T] { } } +test "take_option" { + let queue : Queue[Int] = Queue::[1, 2, 3, 4] + @assertion.assert_eq(queue.take_option(), Some(1))? + @assertion.assert_eq(queue.take_option(), Some(2))? + @assertion.assert_eq(queue.take_option(), Some(3))? + @assertion.assert_eq(queue.take_option(), Some(4))? + @assertion.assert_eq(queue.take_option(), None)? +} + pub fn iter[T : Eq](self : Queue[T], f : (T) -> Unit) { if self.length > 0 { let tail = self.tail.unwrap() - fn helper(cell : Cell[T]) { - f(cell.content) - if (cell == tail).not() { - helper(cell.next.unwrap()) + loop tail.next.unwrap() { + cell => { + f(cell.content) + if (cell == tail).not() { + continue cell.next.unwrap() + } } } - - helper(tail.next.unwrap()) } } +test "iter" { + let queue : Queue[Int] = create() + let mut sum = 0 + queue.iter(fn(x) { sum = sum + x }) + @assertion.assert_eq(sum, 0)? + queue.add(1) + queue.add(2) + queue.add(3) + queue.add(4) + sum = 0 + queue.iter(fn(x) { sum = sum + x }) + @assertion.assert_eq(sum, 10)? +} + pub fn fold[T : Eq](self : Queue[T], init : T, f : (T, T) -> T) -> T { if self.length == 0 { init } else { let tail = self.tail.unwrap() - fn helper(cell : Cell[T], acc : T) -> T { - let new_acc = f(acc, cell.content) - if cell == tail { - new_acc - } else { - helper(cell.next.unwrap(), new_acc) + loop tail.next.unwrap(), init { + cell, acc => { + let new_acc = f(acc, cell.content) + if cell == tail { + new_acc + } else { + continue cell.next.unwrap(), + new_acc + } } } - helper(tail.next.unwrap(), init) } } +test "fold" { + let queue : Queue[Int] = create() + let sum = queue.fold(0, fn(acc, x) { acc + x }) + @assertion.assert_eq(sum, 0)? + queue.add(1) + queue.add(2) + queue.add(3) + queue.add(4) + let sum = queue.fold(0, fn(acc, x) { acc + x }) + @assertion.assert_eq(sum, 10)? +} From 3797c2eaa793ca918fc51ca241f05efe604da0e1 Mon Sep 17 00:00:00 2001 From: CAIMEOX <1853884864@qq.com> Date: Sat, 16 Mar 2024 13:43:19 +0800 Subject: [PATCH 05/23] add docs --- queue/queue.mbt | 81 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/queue/queue.mbt b/queue/queue.mbt index 71f4f9934..cae4d6e90 100644 --- a/queue/queue.mbt +++ b/queue/queue.mbt @@ -12,6 +12,12 @@ pub struct Queue[T] { mut tail : Option[Cell[T]] } +/// Creates a new empty queue. +/// +/// # Example +/// ``` +/// let queue : Queue[Int] = Queue::create() +/// ``` pub fn Queue::create[T]() -> Queue[T] { { length: 0, tail: None } } @@ -22,6 +28,12 @@ test "create" { @assertion.assert_true(queue.tail == None)? } +/// Creates a new queue from an array. +/// +/// # Example +/// ``` +/// let queue : Queue[Int] = Queue::[1, 2, 3, 4] +/// ``` pub fn Queue::from_array[T](array : Array[T]) -> Queue[T] { let queue : Queue[T] = create() for i = 0; i < array.length(); i = i + 1 { @@ -40,6 +52,12 @@ test "from_array" { @assertion.assert_eq(queue.length, 0)? } +/// Creates a new queue from a list. +/// +/// # Example +/// ``` +/// let queue : Queue[Int] = Queue::from_list(Cons(1, Cons(2, Cons(3, Cons(4, Nil)))) +/// ``` pub fn Queue::from_list[T](list : List[T]) -> Queue[T] { let queue : Queue[T] = create() loop list { @@ -67,6 +85,13 @@ test "from_list" { @assertion.assert_eq(queue.length, 0)? } +/// Clears the queue. +/// +/// # Example +/// ``` +/// let queue : Queue[Int] = Queue::[1, 2, 3, 4] +/// queue.clear() +/// ``` pub fn clear[T](self : Queue[T]) { self.length = 0 self.tail = None @@ -79,6 +104,13 @@ test "clear" { @assertion.assert_true(queue.tail == None)? } +/// Adds a value to the queue. +/// +/// # Example +/// ``` +/// let queue : Queue[Int] = Queue::create() +/// queue.add(1) +/// ``` pub fn add[T](self : Queue[T], value : T) { if self.length == 0 { let cell : Cell[T] = { content: value, next: None } @@ -109,6 +141,13 @@ test "add" { @assertion.assert_eq(queue.length, 0)? } +/// Peeks at the first value in the queue. +/// +/// # Example +/// ``` +/// let queue : Queue[Int] = Queue::[1, 2, 3, 4] +/// let first = queue.peek() +/// ``` pub fn peek[T](self : Queue[T]) -> Option[T] { let tail = self.tail match tail { @@ -141,6 +180,13 @@ test "peek" { @assertion.assert_eq(queue.peek(), None)? } +/// Returns the length of the queue. +/// +/// # Example +/// ``` +/// let queue : Queue[Int] = Queue::[1, 2, 3, 4] +/// let length = queue.length() +/// ``` pub fn length[T](self : Queue[T]) -> Int { self.length } @@ -155,6 +201,13 @@ test "length" { @assertion.assert_eq(queue.length(), 4)? } +/// Checks if the queue is empty. +/// +/// # Example +/// ``` +/// let queue : Queue[Int] = Queue::create() +/// let is_empty = queue.is_empty() +/// ``` pub fn is_empty[T](self : Queue[T]) -> Bool { self.length == 0 } @@ -169,6 +222,13 @@ test "is_empty" { @assertion.assert_false(queue.is_empty())? } +/// Takes the first value from the queue. +/// +/// # Example +/// ``` +/// let queue : Queue[Int] = Queue::[1, 2, 3, 4] +/// let first = queue.take() +/// ``` pub fn take[T : Eq](self : Queue[T]) -> T { if self.length == 0 { abort("Queue is empty") @@ -193,6 +253,13 @@ test "take" { @assertion.assert_eq(queue.length(), 0)? } +/// Takes the first value from the queue, or returns `None` if the queue is empty. +/// +/// # Example +/// ``` +/// let queue : Queue[Int] = Queue::[1, 2, 3, 4] +/// let first = queue.take_option() +/// ``` pub fn take_option[T : Eq](self : Queue[T]) -> Option[T] { if self.length == 0 { None @@ -210,6 +277,13 @@ test "take_option" { @assertion.assert_eq(queue.take_option(), 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 : Eq](self : Queue[T], f : (T) -> Unit) { if self.length > 0 { let tail = self.tail.unwrap() @@ -238,6 +312,13 @@ test "iter" { @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 : Eq](self : Queue[T], init : T, f : (T, T) -> T) -> T { if self.length == 0 { init From 4f47c43c2955f9a4243c9c817ee5f8f75a44dbc6 Mon Sep 17 00:00:00 2001 From: CAIMEOX <1853884864@qq.com> Date: Sun, 17 Mar 2024 10:30:30 +0800 Subject: [PATCH 06/23] add license --- queue/queue.mbt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/queue/queue.mbt b/queue/queue.mbt index cae4d6e90..8c81f4790 100644 --- a/queue/queue.mbt +++ b/queue/queue.mbt @@ -1,3 +1,17 @@ +// 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 Cell[T] { content : T mut next : Option[Cell[T]] From 9f5653807339efbd24cc5290345ca17afdbb41ff Mon Sep 17 00:00:00 2001 From: CAIMEOX <1853884864@qq.com> Date: Sun, 17 Mar 2024 10:33:35 +0800 Subject: [PATCH 07/23] remove pub keyword for Queue rename create to new to keep it consistent with other code --- queue/queue.mbt | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/queue/queue.mbt b/queue/queue.mbt index 8c81f4790..f42cc5f5e 100644 --- a/queue/queue.mbt +++ b/queue/queue.mbt @@ -21,7 +21,7 @@ pub fn op_equal[T : Eq](self : Cell[T], other : Cell[T]) -> Bool { self.content == other.content } -pub struct Queue[T] { +struct Queue[T] { mut length : Int mut tail : Option[Cell[T]] } @@ -32,12 +32,12 @@ pub struct Queue[T] { /// ``` /// let queue : Queue[Int] = Queue::create() /// ``` -pub fn Queue::create[T]() -> Queue[T] { +pub fn Queue::new[T]() -> Queue[T] { { length: 0, tail: None } } -test "create" { - let queue : Queue[Int] = create() +test "new" { + let queue : Queue[Int] = new() @assertion.assert_eq(queue.length, 0)? @assertion.assert_true(queue.tail == None)? } @@ -49,7 +49,7 @@ test "create" { /// let queue : Queue[Int] = Queue::[1, 2, 3, 4] /// ``` pub fn Queue::from_array[T](array : Array[T]) -> Queue[T] { - let queue : Queue[T] = create() + let queue : Queue[T] = new() for i = 0; i < array.length(); i = i + 1 { queue.add(array[i]) } @@ -73,7 +73,7 @@ test "from_array" { /// let queue : Queue[Int] = Queue::from_list(Cons(1, Cons(2, Cons(3, Cons(4, Nil)))) /// ``` pub fn Queue::from_list[T](list : List[T]) -> Queue[T] { - let queue : Queue[T] = create() + let queue : Queue[T] = new() loop list { l => match l { @@ -142,7 +142,7 @@ pub fn add[T](self : Queue[T], value : T) { } test "add" { - let queue : Queue[Int] = create() + let queue : Queue[Int] = new() queue.add(1) queue.add(2) queue.add(3) @@ -177,7 +177,7 @@ pub fn peek[T](self : Queue[T]) -> Option[T] { } test "peek" { - let queue : Queue[Int] = create() + let queue : Queue[Int] = new() @assertion.assert_eq(queue.peek(), None)? queue.add(1) queue.add(2) @@ -206,7 +206,7 @@ pub fn length[T](self : Queue[T]) -> Int { } test "length" { - let queue : Queue[Int] = create() + let queue : Queue[Int] = new() @assertion.assert_eq(queue.length(), 0)? queue.add(1) queue.add(2) @@ -227,7 +227,7 @@ pub fn is_empty[T](self : Queue[T]) -> Bool { } test "is_empty" { - let queue : Queue[Int] = create() + let queue : Queue[Int] = new() @assertion.assert_true(queue.is_empty())? queue.add(1) queue.add(2) @@ -313,7 +313,7 @@ pub fn iter[T : Eq](self : Queue[T], f : (T) -> Unit) { } test "iter" { - let queue : Queue[Int] = create() + let queue : Queue[Int] = new() let mut sum = 0 queue.iter(fn(x) { sum = sum + x }) @assertion.assert_eq(sum, 0)? @@ -353,7 +353,7 @@ pub fn fold[T : Eq](self : Queue[T], init : T, f : (T, T) -> T) -> T { } test "fold" { - let queue : Queue[Int] = create() + let queue : Queue[Int] = new() let sum = queue.fold(0, fn(acc, x) { acc + x }) @assertion.assert_eq(sum, 0)? queue.add(1) From d0c78df269a1562156d2351f88d67786548d9646 Mon Sep 17 00:00:00 2001 From: CAIMEOX <1853884864@qq.com> Date: Mon, 18 Mar 2024 12:47:29 +0800 Subject: [PATCH 08/23] rename take to pop --- queue/queue.mbt | 68 ++++++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/queue/queue.mbt b/queue/queue.mbt index f42cc5f5e..718220b20 100644 --- a/queue/queue.mbt +++ b/queue/queue.mbt @@ -59,10 +59,10 @@ pub fn Queue::from_array[T](array : Array[T]) -> Queue[T] { test "from_array" { let queue : Queue[Int] = Queue::[1, 2, 3, 4] @assertion.assert_eq(queue.length, 4)? - @assertion.assert_eq(queue.take(), 1)? - @assertion.assert_eq(queue.take(), 2)? - @assertion.assert_eq(queue.take(), 3)? - @assertion.assert_eq(queue.take(), 4)? + @assertion.assert_eq(queue.pop(), 1)? + @assertion.assert_eq(queue.pop(), 2)? + @assertion.assert_eq(queue.pop(), 3)? + @assertion.assert_eq(queue.pop(), 4)? @assertion.assert_eq(queue.length, 0)? } @@ -92,10 +92,10 @@ test "from_list" { Cons(1, Cons(2, Cons(3, Cons(4, Nil)))), ) @assertion.assert_eq(queue.length, 4)? - @assertion.assert_eq(queue.take(), 1)? - @assertion.assert_eq(queue.take(), 2)? - @assertion.assert_eq(queue.take(), 3)? - @assertion.assert_eq(queue.take(), 4)? + @assertion.assert_eq(queue.pop(), 1)? + @assertion.assert_eq(queue.pop(), 2)? + @assertion.assert_eq(queue.pop(), 3)? + @assertion.assert_eq(queue.pop(), 4)? @assertion.assert_eq(queue.length, 0)? } @@ -148,10 +148,10 @@ test "add" { queue.add(3) queue.add(4) @assertion.assert_eq(queue.length, 4)? - @assertion.assert_eq(queue.take(), 1)? - @assertion.assert_eq(queue.take(), 2)? - @assertion.assert_eq(queue.take(), 3)? - @assertion.assert_eq(queue.take(), 4)? + @assertion.assert_eq(queue.pop(), 1)? + @assertion.assert_eq(queue.pop(), 2)? + @assertion.assert_eq(queue.pop(), 3)? + @assertion.assert_eq(queue.pop(), 4)? @assertion.assert_eq(queue.length, 0)? } @@ -184,13 +184,13 @@ test "peek" { queue.add(3) queue.add(4) @assertion.assert_eq(queue.peek(), Some(1))? - @assertion.assert_eq(queue.take(), 1)? + @assertion.assert_eq(queue.pop(), 1)? @assertion.assert_eq(queue.peek(), Some(2))? - @assertion.assert_eq(queue.take(), 2)? + @assertion.assert_eq(queue.pop(), 2)? @assertion.assert_eq(queue.peek(), Some(3))? - @assertion.assert_eq(queue.take(), 3)? + @assertion.assert_eq(queue.pop(), 3)? @assertion.assert_eq(queue.peek(), Some(4))? - @assertion.assert_eq(queue.take(), 4)? + @assertion.assert_eq(queue.pop(), 4)? @assertion.assert_eq(queue.peek(), None)? } @@ -236,14 +236,14 @@ test "is_empty" { @assertion.assert_false(queue.is_empty())? } -/// Takes the first value from the queue. +/// Pops the first value from the queue. /// /// # Example /// ``` /// let queue : Queue[Int] = Queue::[1, 2, 3, 4] -/// let first = queue.take() +/// let first = queue.pop() /// ``` -pub fn take[T : Eq](self : Queue[T]) -> T { +pub fn pop[T : Eq](self : Queue[T]) -> T { if self.length == 0 { abort("Queue is empty") } @@ -258,37 +258,37 @@ pub fn take[T : Eq](self : Queue[T]) -> T { head.content } -test "take" { +test "pop" { let queue : Queue[Int] = Queue::[1, 2, 3, 4] - @assertion.assert_eq(queue.take(), 1)? - @assertion.assert_eq(queue.take(), 2)? - @assertion.assert_eq(queue.take(), 3)? - @assertion.assert_eq(queue.take(), 4)? + @assertion.assert_eq(queue.pop(), 1)? + @assertion.assert_eq(queue.pop(), 2)? + @assertion.assert_eq(queue.pop(), 3)? + @assertion.assert_eq(queue.pop(), 4)? @assertion.assert_eq(queue.length(), 0)? } -/// Takes the first value from the queue, or returns `None` if the queue is empty. +/// Pops the first value from the queue, or returns `None` if the queue is empty. /// /// # Example /// ``` /// let queue : Queue[Int] = Queue::[1, 2, 3, 4] -/// let first = queue.take_option() +/// let first = queue.pop_option() /// ``` -pub fn take_option[T : Eq](self : Queue[T]) -> Option[T] { +pub fn pop_option[T : Eq](self : Queue[T]) -> Option[T] { if self.length == 0 { None } else { - Some(self.take()) + Some(self.pop()) } } -test "take_option" { +test "pop_option" { let queue : Queue[Int] = Queue::[1, 2, 3, 4] - @assertion.assert_eq(queue.take_option(), Some(1))? - @assertion.assert_eq(queue.take_option(), Some(2))? - @assertion.assert_eq(queue.take_option(), Some(3))? - @assertion.assert_eq(queue.take_option(), Some(4))? - @assertion.assert_eq(queue.take_option(), None)? + @assertion.assert_eq(queue.pop_option(), Some(1))? + @assertion.assert_eq(queue.pop_option(), Some(2))? + @assertion.assert_eq(queue.pop_option(), Some(3))? + @assertion.assert_eq(queue.pop_option(), Some(4))? + @assertion.assert_eq(queue.pop_option(), None)? } /// Iterates over the queue. From 4f10ede7b030ca9db05498bdd5de127d3d5c6564 Mon Sep 17 00:00:00 2001 From: HongboZhang Date: Mon, 18 Mar 2024 21:04:04 +0800 Subject: [PATCH 09/23] fix CI --- queue/queue.mbt | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/queue/queue.mbt b/queue/queue.mbt index 718220b20..d6c650392 100644 --- a/queue/queue.mbt +++ b/queue/queue.mbt @@ -106,7 +106,7 @@ test "from_list" { /// let queue : Queue[Int] = Queue::[1, 2, 3, 4] /// queue.clear() /// ``` -pub fn clear[T](self : Queue[T]) { +pub fn clear[T](self : Queue[T]) -> Unit { self.length = 0 self.tail = None } @@ -125,7 +125,7 @@ test "clear" { /// let queue : Queue[Int] = Queue::create() /// queue.add(1) /// ``` -pub fn add[T](self : Queue[T], value : T) { +pub fn add[T](self : Queue[T], value : T) -> Unit { if self.length == 0 { let cell : Cell[T] = { content: value, next: None } cell.next = Some(cell) @@ -298,7 +298,7 @@ test "pop_option" { /// let queue : Queue[Int] = Queue::[1, 2, 3, 4] /// queue.iter(fn(x) { print(x) }) /// ``` -pub fn iter[T : Eq](self : Queue[T], f : (T) -> Unit) { +pub fn iter[T : Eq](self : Queue[T], f : (T) -> Unit) -> Unit { if self.length > 0 { let tail = self.tail.unwrap() loop tail.next.unwrap() { @@ -344,8 +344,7 @@ pub fn fold[T : Eq](self : Queue[T], init : T, f : (T, T) -> T) -> T { if cell == tail { new_acc } else { - continue cell.next.unwrap(), - new_acc + continue cell.next.unwrap(), new_acc } } } From 83f756ce0ef58368004c5caa0980245b75edb87e Mon Sep 17 00:00:00 2001 From: CAIMEOX <1853884864@qq.com> Date: Mon, 18 Mar 2024 22:58:39 +0800 Subject: [PATCH 10/23] rewrite peek and remove break --- queue/queue.mbt | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/queue/queue.mbt b/queue/queue.mbt index d6c650392..ef38dcf09 100644 --- a/queue/queue.mbt +++ b/queue/queue.mbt @@ -21,6 +21,7 @@ pub fn op_equal[T : Eq](self : Cell[T], other : Cell[T]) -> Bool { self.content == other.content } + struct Queue[T] { mut length : Int mut tail : Option[Cell[T]] @@ -81,7 +82,7 @@ pub fn Queue::from_list[T](list : List[T]) -> Queue[T] { queue.add(head) continue tail } - Nil => break + Nil => () } } queue @@ -163,16 +164,10 @@ test "add" { /// let first = queue.peek() /// ``` pub fn peek[T](self : Queue[T]) -> Option[T] { - let tail = self.tail - match tail { - Some(tail) => { - let head = tail.next - match head { - Some(head) => Some(head.content) - None => None - } - } - None => None + if self.length == 0 { + return None + } else { + Some(self.tail.unwrap().next.unwrap().content) } } From fa2ce54903e9ef9a220e2490150a621b6063210f Mon Sep 17 00:00:00 2001 From: CAIMEOX <1853884864@qq.com> Date: Tue, 19 Mar 2024 13:00:11 +0800 Subject: [PATCH 11/23] refactor the queue --- queue/queue.mbt | 300 +++++++++++++++++++----------------------------- 1 file changed, 119 insertions(+), 181 deletions(-) diff --git a/queue/queue.mbt b/queue/queue.mbt index ef38dcf09..215f0893c 100644 --- a/queue/queue.mbt +++ b/queue/queue.mbt @@ -12,19 +12,20 @@ // See the License for the specific language governing permissions and // limitations under the License. -struct Cell[T] { +struct Cont[T] { content : T - mut next : Option[Cell[T]] + mut next : Cell[T] } -pub fn op_equal[T : Eq](self : Cell[T], other : Cell[T]) -> Bool { - self.content == other.content +enum Cell[T] { + Nil + Cons(Cont[T]) } - struct Queue[T] { mut length : Int - mut tail : Option[Cell[T]] + mut first : Cell[T] + mut last : Cell[T] } /// Creates a new empty queue. @@ -34,89 +35,65 @@ struct Queue[T] { /// let queue : Queue[Int] = Queue::create() /// ``` pub fn Queue::new[T]() -> Queue[T] { - { length: 0, tail: None } + { length: 0, first: Cell::Nil, last: Cell::Nil } } test "new" { let queue : Queue[Int] = new() @assertion.assert_eq(queue.length, 0)? - @assertion.assert_true(queue.tail == None)? } -/// Creates a new queue from an array. -/// -/// # Example -/// ``` -/// let queue : Queue[Int] = Queue::[1, 2, 3, 4] -/// ``` -pub fn Queue::from_array[T](array : Array[T]) -> Queue[T] { - let queue : Queue[T] = new() - for i = 0; i < array.length(); i = i + 1 { - queue.add(array[i]) +pub fn Queue::from_array[T](arr : Array[T]) -> Queue[T] { + let queue : Queue[T] = Queue::new() + for i = 0; i < arr.length() ; i = i + 1 { + queue.push(arr[i]) } queue } -test "from_array" { - let queue : Queue[Int] = Queue::[1, 2, 3, 4] - @assertion.assert_eq(queue.length, 4)? - @assertion.assert_eq(queue.pop(), 1)? - @assertion.assert_eq(queue.pop(), 2)? - @assertion.assert_eq(queue.pop(), 3)? - @assertion.assert_eq(queue.pop(), 4)? - @assertion.assert_eq(queue.length, 0)? -} - -/// Creates a new queue from a list. +/// Clears the queue. /// /// # Example /// ``` -/// let queue : Queue[Int] = Queue::from_list(Cons(1, Cons(2, Cons(3, Cons(4, Nil)))) +/// let queue : Queue[Int] = Queue::[1, 2, 3, 4] +/// queue.clear() /// ``` -pub fn Queue::from_list[T](list : List[T]) -> Queue[T] { - let queue : Queue[T] = new() - loop list { - l => - match l { - Cons(head, tail) => { - queue.add(head) - continue tail - } - Nil => () - } - } - queue +pub fn clear[T](self : Queue[T]) -> Unit { + self.length = 0 + self.first = Cell::Nil + self.last = Cell::Nil } -test "from_list" { - let queue : Queue[Int] = Queue::from_list( - Cons(1, Cons(2, Cons(3, Cons(4, Nil)))), - ) +test "clear" { + let queue : Queue[Int] = Queue::[1, 2, 3, 4] @assertion.assert_eq(queue.length, 4)? - @assertion.assert_eq(queue.pop(), 1)? - @assertion.assert_eq(queue.pop(), 2)? - @assertion.assert_eq(queue.pop(), 3)? - @assertion.assert_eq(queue.pop(), 4)? + queue.clear() @assertion.assert_eq(queue.length, 0)? } -/// Clears the queue. +pub fn length[T](self : Queue[T]) -> Int { + self.length +} + +/// Checks if the queue is empty. /// /// # Example /// ``` -/// let queue : Queue[Int] = Queue::[1, 2, 3, 4] -/// queue.clear() +/// let queue : Queue[Int] = Queue::create() +/// let is_empty = queue.is_empty() /// ``` -pub fn clear[T](self : Queue[T]) -> Unit { - self.length = 0 - self.tail = None +pub fn is_empty[T](self : Queue[T]) -> Bool { + self.length == 0 } -test "clear" { - let queue : Queue[Int] = Queue::[1, 2, 3, 4] - queue.clear() - @assertion.assert_eq(queue.length, 0)? - @assertion.assert_true(queue.tail == None)? +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. @@ -126,34 +103,33 @@ test "clear" { /// let queue : Queue[Int] = Queue::create() /// queue.add(1) /// ``` -pub fn add[T](self : Queue[T], value : T) -> Unit { - if self.length == 0 { - let cell : Cell[T] = { content: value, next: None } - cell.next = Some(cell) - self.length = 1 - self.tail = Some(cell) - } else { - let tail = self.tail.unwrap() - let head = tail.next - let cell = { content: value, next: head } - self.length = self.length + 1 - tail.next = Some(cell) - self.tail = Some(cell) +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 "add" { - let queue : Queue[Int] = new() - queue.add(1) - queue.add(2) - queue.add(3) - queue.add(4) +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(), 1)? @assertion.assert_eq(queue.pop(), 2)? @assertion.assert_eq(queue.pop(), 3)? - @assertion.assert_eq(queue.pop(), 4)? - @assertion.assert_eq(queue.length, 0)? + @assertion.assert_eq(queue.pop(), 1)? } /// Peeks at the first value in the queue. @@ -163,72 +139,41 @@ test "add" { /// let queue : Queue[Int] = Queue::[1, 2, 3, 4] /// let first = queue.peek() /// ``` -pub fn peek[T](self : Queue[T]) -> Option[T] { - if self.length == 0 { - return None - } else { - Some(self.tail.unwrap().next.unwrap().content) +pub fn peek[T](self : Queue[T]) -> T { + match self.first { + Nil => abort("Queue is empty") + Cons(first) => first.content } } test "peek" { - let queue : Queue[Int] = new() - @assertion.assert_eq(queue.peek(), None)? - queue.add(1) - queue.add(2) - queue.add(3) - queue.add(4) - @assertion.assert_eq(queue.peek(), Some(1))? + let queue : Queue[Int] = Queue::[1, 2, 3, 4] + @assertion.assert_eq(queue.peek(), 1)? + @assertion.assert_eq(queue.length, 4)? @assertion.assert_eq(queue.pop(), 1)? - @assertion.assert_eq(queue.peek(), Some(2))? - @assertion.assert_eq(queue.pop(), 2)? - @assertion.assert_eq(queue.peek(), Some(3))? - @assertion.assert_eq(queue.pop(), 3)? - @assertion.assert_eq(queue.peek(), Some(4))? - @assertion.assert_eq(queue.pop(), 4)? - @assertion.assert_eq(queue.peek(), None)? + @assertion.assert_eq(queue.peek(), 2)? + @assertion.assert_eq(queue.length, 3)? } -/// Returns the length of the queue. +/// 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 length = queue.length() -/// ``` -pub fn length[T](self : Queue[T]) -> Int { - self.length -} - -test "length" { - let queue : Queue[Int] = new() - @assertion.assert_eq(queue.length(), 0)? - queue.add(1) - queue.add(2) - queue.add(3) - queue.add(4) - @assertion.assert_eq(queue.length(), 4)? -} - -/// Checks if the queue is empty. -/// -/// # Example +/// let first = queue.peek_option() /// ``` -/// let queue : Queue[Int] = Queue::create() -/// let is_empty = queue.is_empty() -/// ``` -pub fn is_empty[T](self : Queue[T]) -> Bool { - self.length == 0 +pub fn peek_option[T](self : Queue[T]) -> Option[T] { + match self.first { + Nil => None + Cons(first) => Some(first.content) + } } -test "is_empty" { - let queue : Queue[Int] = new() - @assertion.assert_true(queue.is_empty())? - queue.add(1) - queue.add(2) - queue.add(3) - queue.add(4) - @assertion.assert_false(queue.is_empty())? +test "peek_option" { + let queue : Queue[Int] = Queue::[1, 2, 3, 4] + @assertion.assert_eq(queue.peek_option(), Some(1))? + queue.clear() + @assertion.assert_eq(queue.peek_option(), None)? } /// Pops the first value from the queue. @@ -238,19 +183,19 @@ test "is_empty" { /// let queue : Queue[Int] = Queue::[1, 2, 3, 4] /// let first = queue.pop() /// ``` -pub fn pop[T : Eq](self : Queue[T]) -> T { - if self.length == 0 { - abort("Queue is empty") - } - self.length = self.length - 1 - let tail = self.tail.unwrap() - let head = tail.next.unwrap() - if head == tail { - self.tail = None - } else { - tail.next = head.next +pub fn pop[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 + } } - head.content } test "pop" { @@ -259,17 +204,18 @@ test "pop" { @assertion.assert_eq(queue.pop(), 2)? @assertion.assert_eq(queue.pop(), 3)? @assertion.assert_eq(queue.pop(), 4)? - @assertion.assert_eq(queue.length(), 0)? + @assertion.assert_eq(queue.length, 0)? + } -/// Pops the first value from the queue, or returns `None` if the queue is empty. +/// 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_option() /// ``` -pub fn pop_option[T : Eq](self : Queue[T]) -> Option[T] { +pub fn pop_option[T](self : Queue[T]) -> Option[T] { if self.length == 0 { None } else { @@ -293,17 +239,16 @@ test "pop_option" { /// let queue : Queue[Int] = Queue::[1, 2, 3, 4] /// queue.iter(fn(x) { print(x) }) /// ``` -pub fn iter[T : Eq](self : Queue[T], f : (T) -> Unit) -> Unit { - if self.length > 0 { - let tail = self.tail.unwrap() - loop tail.next.unwrap() { - cell => { - f(cell.content) - if (cell == tail).not() { - continue cell.next.unwrap() +pub fn iter[T](self : Queue[T], f : (T) -> Unit) -> Unit { + loop self.first { + cell => + match cell { + Nil => () + Cons({ content, next }) => { + f(content) + continue next } } - } } } @@ -312,10 +257,10 @@ test "iter" { let mut sum = 0 queue.iter(fn(x) { sum = sum + x }) @assertion.assert_eq(sum, 0)? - queue.add(1) - queue.add(2) - queue.add(3) - queue.add(4) + 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)? @@ -328,32 +273,25 @@ test "iter" { /// let queue : Queue[Int] = Queue::[1, 2, 3, 4] /// let sum = queue.fold(0, fn(acc, x) { acc + x }) /// ``` -pub fn fold[T : Eq](self : Queue[T], init : T, f : (T, T) -> T) -> T { - if self.length == 0 { - init - } else { - let tail = self.tail.unwrap() - loop tail.next.unwrap(), init { - cell, acc => { - let new_acc = f(acc, cell.content) - if cell == tail { - new_acc - } else { - continue cell.next.unwrap(), new_acc - } +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(0, fn(acc, x) { acc + x }) @assertion.assert_eq(sum, 0)? - queue.add(1) - queue.add(2) - queue.add(3) - queue.add(4) + queue.push(1) + queue.push(2) + queue.push(3) + queue.push(4) let sum = queue.fold(0, fn(acc, x) { acc + x }) @assertion.assert_eq(sum, 10)? } From 0f557e5c48f9b8767116ca00d1331c64311fbc2d Mon Sep 17 00:00:00 2001 From: CAIMEOX <1853884864@qq.com> Date: Tue, 19 Mar 2024 13:23:38 +0800 Subject: [PATCH 12/23] more efficient `from_array` --- queue/queue.mbt | 46 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/queue/queue.mbt b/queue/queue.mbt index 215f0893c..a163be5cf 100644 --- a/queue/queue.mbt +++ b/queue/queue.mbt @@ -32,7 +32,7 @@ struct Queue[T] { /// /// # Example /// ``` -/// let queue : Queue[Int] = Queue::create() +/// let queue : Queue[Int] = Queue::new() /// ``` pub fn Queue::new[T]() -> Queue[T] { { length: 0, first: Cell::Nil, last: Cell::Nil } @@ -43,14 +43,48 @@ test "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() - for i = 0; i < arr.length() ; i = i + 1 { - queue.push(arr[i]) + 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 { + match (self, other) { + (Nil, Nil) => true + (Cons({ content: x, next: xs }), Cons({ content: y, next: ys })) => + x == y && xs == ys + (_, _) => false + } +} + +test "from_array" { + let queue : Queue[Int] = Queue::[1, 2, 3, 4] + let queue2 : Queue[Int] = Queue::from_array([1]) + @assertion.assert_eq(queue.length, 4)? + @assertion.assert_eq(queue.pop(), 1)? + @assertion.assert_eq(queue.pop(), 2)? + @assertion.assert_eq(queue.pop(), 3)? + @assertion.assert_eq(queue.pop(), 4)? + @assertion.assert_eq(queue2.pop(), 1)? + @assertion.assert_eq(queue2.length, 0)? +} + /// Clears the queue. /// /// # Example @@ -79,7 +113,7 @@ pub fn length[T](self : Queue[T]) -> Int { /// /// # Example /// ``` -/// let queue : Queue[Int] = Queue::create() +/// let queue : Queue[Int] = Queue::new() /// let is_empty = queue.is_empty() /// ``` pub fn is_empty[T](self : Queue[T]) -> Bool { @@ -100,7 +134,7 @@ test "is_empty" { /// /// # Example /// ``` -/// let queue : Queue[Int] = Queue::create() +/// let queue : Queue[Int] = Queue::new() /// queue.add(1) /// ``` pub fn push[T](self : Queue[T], x : T) -> Unit { @@ -205,7 +239,6 @@ test "pop" { @assertion.assert_eq(queue.pop(), 3)? @assertion.assert_eq(queue.pop(), 4)? @assertion.assert_eq(queue.length, 0)? - } /// Pops the first value from the queue, which returns None if the queue is empty. @@ -283,7 +316,6 @@ pub fn fold[T](self : Queue[T], init : T, f : (T, T) -> T) -> T { } } - test "fold" { let queue : Queue[Int] = new() let sum = queue.fold(0, fn(acc, x) { acc + x }) From 520df5d7c4b71e8937e06902b720140495a81f9e Mon Sep 17 00:00:00 2001 From: CAIMEOX <1853884864@qq.com> Date: Tue, 19 Mar 2024 13:32:39 +0800 Subject: [PATCH 13/23] docs: add README --- queue/README.md | 61 +++++++++++++++++++++++++++++++++++++++++++++++++ queue/queue.mbt | 2 ++ 2 files changed, 63 insertions(+) create mode 100644 queue/README.md diff --git a/queue/README.md b/queue/README.md new file mode 100644 index 000000000..9f6216577 --- /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.from_array([1, 2, 3]) +let queue3 = Queue::[1,1,4] +``` + +To clear the queue, you can use the `clear` method. +```moonbit +let queue = Queue.from_array([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.from_array([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. +Remind that the `pop` method will panic if the queue is empty, you can use the `pop_option` method to avoid this. +```moonbit +let queue = Queue.new() +queue.push(1) +queue.push(2) +queue.push(3) +queue.pop() // 1 +queue.pop() // 2 +queue.pop() // 3 +``` + +### Peek +You can get the first element of the queue without removing it using the `peek` method. +Remind that the `peek` method will panic if the queue is empty, you can use the `peek_option` method to avoid this. +```moonbit +let queue = Queue.from_array([1, 2, 3]) +queue.peek() // 1 +``` + +### Traverse +You can traverse the queue using the `iter` method. +```moonbit +let queue = Queue.from_array([1, 2, 3]) +let mut sum = 0 +queue.iter(fn(x) { sum = sum + x }) // sum = 6 +``` + +You can fold the queue using the `fold` method. +```moonbit +let queue = Queue.from_array([1, 2, 3]) +let sum = queue.fold(0, fn(acc, x) { acc + x }) // sum = 6 +``` \ No newline at end of file diff --git a/queue/queue.mbt b/queue/queue.mbt index a163be5cf..7af79911d 100644 --- a/queue/queue.mbt +++ b/queue/queue.mbt @@ -326,4 +326,6 @@ test "fold" { queue.push(4) let sum = queue.fold(0, fn(acc, x) { acc + x }) @assertion.assert_eq(sum, 10)? + let sum2 = Queue::[1,2,3,4].fold(0, fn(acc, x) { acc + x }) + @assertion.assert_eq(sum2, 10)? } From ce0288bca2239c2056174b09c967725a2e2d4254 Mon Sep 17 00:00:00 2001 From: CAIMEOX <1853884864@qq.com> Date: Tue, 19 Mar 2024 15:11:01 +0800 Subject: [PATCH 14/23] fix docs use loop instead of recursion rename `peek` to `peek_exn` and `peek_option` to `peek` --- queue/README.md | 24 +++++++++---------- queue/queue.mbt | 64 +++++++++++++++++++++++++------------------------ 2 files changed, 45 insertions(+), 43 deletions(-) diff --git a/queue/README.md b/queue/README.md index 9f6216577..72af19a11 100644 --- a/queue/README.md +++ b/queue/README.md @@ -6,21 +6,21 @@ Queue is a first in first out (FIFO) data structure, allowing to process their e ### 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.from_array([1, 2, 3]) +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.from_array([1, 2, 3]) +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.from_array([1, 2, 3]) +let queue = Queue::[1,2,3] queue.length() // 3 queue.is_empty() // false ``` @@ -29,7 +29,7 @@ queue.is_empty() // false You can add elements to the queue using the `push` method and remove them using the `pop` method. Remind that the `pop` method will panic if the queue is empty, you can use the `pop_option` method to avoid this. ```moonbit -let queue = Queue.new() +let queue = Queue::new() queue.push(1) queue.push(2) queue.push(3) @@ -40,22 +40,22 @@ queue.pop() // 3 ### Peek You can get the first element of the queue without removing it using the `peek` method. -Remind that the `peek` method will panic if the queue is empty, you can use the `peek_option` method to avoid this. ```moonbit -let queue = Queue.from_array([1, 2, 3]) -queue.peek() // 1 +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.from_array([1, 2, 3]) +let queue = Queue::[1,2,3] let mut sum = 0 -queue.iter(fn(x) { sum = sum + x }) // sum = 6 +queue.iter(fn(x) { sum += x }) // sum = 6 ``` You can fold the queue using the `fold` method. ```moonbit -let queue = Queue.from_array([1, 2, 3]) -let sum = queue.fold(0, fn(acc, x) { acc + x }) // sum = 6 +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/queue.mbt b/queue/queue.mbt index 7af79911d..164130ede 100644 --- a/queue/queue.mbt +++ b/queue/queue.mbt @@ -35,7 +35,7 @@ struct Queue[T] { /// let queue : Queue[Int] = Queue::new() /// ``` pub fn Queue::new[T]() -> Queue[T] { - { length: 0, first: Cell::Nil, last: Cell::Nil } + { length: 0, first: Nil, last: Nil } } test "new" { @@ -65,17 +65,21 @@ pub fn Queue::from_array[T](arr : Array[T]) -> Queue[T] { /// Tests if two queue cells are equal. pub fn op_equal[T : Eq](self : Cell[T], other : Cell[T]) -> Bool { - match (self, other) { - (Nil, Nil) => true - (Cons({ content: x, next: xs }), Cons({ content: y, next: ys })) => - x == y && xs == ys - (_, _) => false + loop self, other { + Nil, Nil => true + Cons({ content: x, next: xs }), Cons({ content: y, next: ys }) => { + if x != y { + return false + } + continue xs, ys + } + _, _ => false } } test "from_array" { let queue : Queue[Int] = Queue::[1, 2, 3, 4] - let queue2 : Queue[Int] = Queue::from_array([1]) + let queue2 : Queue[Int] = Queue::[1] @assertion.assert_eq(queue.length, 4)? @assertion.assert_eq(queue.pop(), 1)? @assertion.assert_eq(queue.pop(), 2)? @@ -94,8 +98,8 @@ test "from_array" { /// ``` pub fn clear[T](self : Queue[T]) -> Unit { self.length = 0 - self.first = Cell::Nil - self.last = Cell::Nil + self.first = Nil + self.last = Nil } test "clear" { @@ -168,24 +172,25 @@ test "push" { /// 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() +/// let first = queue.peek_exn() /// ``` -pub fn peek[T](self : Queue[T]) -> T { +pub fn peek_exn[T](self : Queue[T]) -> T { match self.first { Nil => abort("Queue is empty") Cons(first) => first.content } } -test "peek" { +test "peek_exn" { let queue : Queue[Int] = Queue::[1, 2, 3, 4] - @assertion.assert_eq(queue.peek(), 1)? + @assertion.assert_eq(queue.peek_exn(), 1)? @assertion.assert_eq(queue.length, 4)? @assertion.assert_eq(queue.pop(), 1)? - @assertion.assert_eq(queue.peek(), 2)? + @assertion.assert_eq(queue.peek_exn(), 2)? @assertion.assert_eq(queue.length, 3)? } @@ -194,20 +199,20 @@ test "peek" { /// # Example /// ``` /// let queue : Queue[Int] = Queue::[1, 2, 3, 4] -/// let first = queue.peek_option() +/// let first = queue.peek() /// ``` -pub fn peek_option[T](self : Queue[T]) -> Option[T] { +pub fn peek[T](self : Queue[T]) -> Option[T] { match self.first { Nil => None Cons(first) => Some(first.content) } } -test "peek_option" { +test "peek" { let queue : Queue[Int] = Queue::[1, 2, 3, 4] - @assertion.assert_eq(queue.peek_option(), Some(1))? + @assertion.assert_eq(queue.peek(), Some(1))? queue.clear() - @assertion.assert_eq(queue.peek_option(), None)? + @assertion.assert_eq(queue.peek(), None)? } /// Pops the first value from the queue. @@ -274,14 +279,11 @@ test "pop_option" { /// ``` pub fn iter[T](self : Queue[T], f : (T) -> Unit) -> Unit { loop self.first { - cell => - match cell { - Nil => () - Cons({ content, next }) => { - f(content) - continue next - } - } + Cons({ content, next }) => { + f(content) + continue next + } + Nil => return } } @@ -306,7 +308,7 @@ test "iter" { /// 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 { +pub fn fold[T](self : Queue[T], ~init : T, f : (T, T) -> T) -> T { loop self.first, init { cell, acc => match cell { @@ -318,14 +320,14 @@ pub fn fold[T](self : Queue[T], init : T, f : (T, T) -> T) -> T { test "fold" { let queue : Queue[Int] = new() - let sum = queue.fold(0, fn(acc, x) { acc + x }) + 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(0, fn(acc, x) { acc + x }) + let sum = queue.fold(~init=0, fn(acc, x) { acc + x }) @assertion.assert_eq(sum, 10)? - let sum2 = Queue::[1,2,3,4].fold(0, fn(acc, x) { acc + x }) + let sum2 = Queue::[1, 2, 3, 4].fold(~init=0, fn(acc, x) { acc + x }) @assertion.assert_eq(sum2, 10)? } From 7bec22a3fd04541bb91ec5439d034f2f5e6196c7 Mon Sep 17 00:00:00 2001 From: CAIMEOX <1853884864@qq.com> Date: Wed, 20 Mar 2024 12:35:29 +0800 Subject: [PATCH 15/23] rename pop_option to pop and pop to pop_exn --- queue/README.md | 8 ++++---- queue/queue.mbt | 53 +++++++++++++++++++++++++------------------------ 2 files changed, 31 insertions(+), 30 deletions(-) diff --git a/queue/README.md b/queue/README.md index 72af19a11..45f7d6879 100644 --- a/queue/README.md +++ b/queue/README.md @@ -27,15 +27,15 @@ 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. -Remind that the `pop` method will panic if the queue is empty, you can use the `pop_option` method to avoid this. +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() // 1 -queue.pop() // 2 -queue.pop() // 3 +queue.pop_exn() // 1 +queue.pop() // Some(2) +queue.pop() // Some(3) ``` ### Peek diff --git a/queue/queue.mbt b/queue/queue.mbt index 164130ede..fa36998d4 100644 --- a/queue/queue.mbt +++ b/queue/queue.mbt @@ -81,11 +81,11 @@ 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(), 1)? - @assertion.assert_eq(queue.pop(), 2)? - @assertion.assert_eq(queue.pop(), 3)? - @assertion.assert_eq(queue.pop(), 4)? - @assertion.assert_eq(queue2.pop(), 1)? + @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)? } @@ -164,10 +164,10 @@ test "push" { queue.push(3) queue.push(1) @assertion.assert_eq(queue.length, 4)? - @assertion.assert_eq(queue.pop(), 1)? - @assertion.assert_eq(queue.pop(), 2)? - @assertion.assert_eq(queue.pop(), 3)? - @assertion.assert_eq(queue.pop(), 1)? + @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. @@ -189,7 +189,7 @@ 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(), 1)? + @assertion.assert_eq(queue.pop_exn(), 1)? @assertion.assert_eq(queue.peek_exn(), 2)? @assertion.assert_eq(queue.length, 3)? } @@ -217,12 +217,13 @@ test "peek" { /// 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() +/// let first = queue.pop_exn() /// ``` -pub fn pop[T](self : Queue[T]) -> T { +pub fn pop_exn[T](self : Queue[T]) -> T { match self.first { Nil => abort("Queue is empty") Cons({ content, next: Nil }) => { @@ -237,12 +238,12 @@ pub fn pop[T](self : Queue[T]) -> T { } } -test "pop" { +test "pop_exn" { let queue : Queue[Int] = Queue::[1, 2, 3, 4] - @assertion.assert_eq(queue.pop(), 1)? - @assertion.assert_eq(queue.pop(), 2)? - @assertion.assert_eq(queue.pop(), 3)? - @assertion.assert_eq(queue.pop(), 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)? } @@ -251,23 +252,23 @@ test "pop" { /// # Example /// ``` /// let queue : Queue[Int] = Queue::[1, 2, 3, 4] -/// let first = queue.pop_option() +/// let first = queue.pop() /// ``` -pub fn pop_option[T](self : Queue[T]) -> Option[T] { +pub fn pop[T](self : Queue[T]) -> Option[T] { if self.length == 0 { None } else { - Some(self.pop()) + Some(self.pop_exn()) } } -test "pop_option" { +test "pop" { let queue : Queue[Int] = Queue::[1, 2, 3, 4] - @assertion.assert_eq(queue.pop_option(), Some(1))? - @assertion.assert_eq(queue.pop_option(), Some(2))? - @assertion.assert_eq(queue.pop_option(), Some(3))? - @assertion.assert_eq(queue.pop_option(), Some(4))? - @assertion.assert_eq(queue.pop_option(), None)? + @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. From 4f76f7bd1cdf223b4c1506ea1e01f6e3d3772a8e Mon Sep 17 00:00:00 2001 From: CAIMEOX <1853884864@qq.com> Date: Wed, 20 Mar 2024 12:40:55 +0800 Subject: [PATCH 16/23] remove unnecessary `return` --- queue/queue.mbt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/queue/queue.mbt b/queue/queue.mbt index fa36998d4..68f64c2c6 100644 --- a/queue/queue.mbt +++ b/queue/queue.mbt @@ -69,7 +69,7 @@ pub fn op_equal[T : Eq](self : Cell[T], other : Cell[T]) -> Bool { Nil, Nil => true Cons({ content: x, next: xs }), Cons({ content: y, next: ys }) => { if x != y { - return false + break false } continue xs, ys } @@ -284,7 +284,7 @@ pub fn iter[T](self : Queue[T], f : (T) -> Unit) -> Unit { f(content) continue next } - Nil => return + Nil => () } } From a9346f45cb32fa4134aba58e3e68cf7ce68b0c01 Mon Sep 17 00:00:00 2001 From: Lampese Date: Tue, 19 Mar 2024 01:25:46 +0800 Subject: [PATCH 17/23] feat: add more function to deque --- deque/README.md | 29 ++++++++++++----- deque/deque.mbt | 82 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 101 insertions(+), 10 deletions(-) diff --git a/deque/README.md b/deque/README.md index 769baf0bf..866b3176a 100644 --- a/deque/README.md +++ b/deque/README.md @@ -90,6 +90,16 @@ 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_ignore()` with `pop_back_ignore()`. + +```moonbit +let dq = Deque::[1, 2, 3, 4, 5] +dq.pop_front_ignore() +dq.front() // Some(2) +dq.pop_back_ignore() +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 +132,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..b6f27de94 100644 --- a/deque/deque.mbt +++ b/deque/deque.mbt @@ -164,6 +164,36 @@ pub fn push_back[T](self : Deque[T], value : T) -> Unit { self.len += 1 } +/// Removes a fornt element from a deque. +/// +/// # Example +/// ``` +/// let dq = Deque::[1, 2, 3, 4, 5] +/// dq.pop_front_ignore() +/// ``` +pub fn pop_front_ignore[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. +/// +/// # Example +/// ``` +/// let dq = Deque::[1, 2, 3, 4, 5] +/// dq.pop_back_ignore() +/// ``` +pub fn pop_back_ignore[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 @@ -317,8 +347,8 @@ test "iter" { test "iter2" { let v = Deque::[1, 2, 3, 4, 5] - v.pop_front() |> ignore - v.pop_back() |> ignore + v.pop_front_ignore() + v.pop_back_ignore() let mut sum = 0 v.iter(fn { x => sum += x }) @assertion.assert_eq(sum, 9)? @@ -489,3 +519,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))? +} From 5cb42ba5587d520d82dcaa8b4a91fdbcee7d044f Mon Sep 17 00:00:00 2001 From: CAIMEOX <1853884864@qq.com> Date: Wed, 20 Mar 2024 12:47:41 +0800 Subject: [PATCH 18/23] rename pop_xxx_ignore to pop_xxx_exn --- deque/README.md | 7 ++++--- deque/deque.mbt | 14 ++++++++------ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/deque/README.md b/deque/README.md index 866b3176a..612c99f4a 100644 --- a/deque/README.md +++ b/deque/README.md @@ -90,13 +90,14 @@ 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_ignore()` with `pop_back_ignore()`. +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_ignore() +dq.pop_front_exn() dq.front() // Some(2) -dq.pop_back_ignore() +dq.pop_back_exn() dq.back() // Some(3) ``` diff --git a/deque/deque.mbt b/deque/deque.mbt index b6f27de94..ef6800b0e 100644 --- a/deque/deque.mbt +++ b/deque/deque.mbt @@ -166,12 +166,13 @@ pub fn push_back[T](self : Deque[T], value : T) -> Unit { /// 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_ignore() +/// dq.pop_front_exn() /// ``` -pub fn pop_front_ignore[T](self : Deque[T]) -> Unit { +pub fn pop_front_exn[T](self : Deque[T]) -> Unit { if self.len == 0 { abort("The deque is empty!") } @@ -181,12 +182,13 @@ pub fn pop_front_ignore[T](self : Deque[T]) -> Unit { /// 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_ignore() +/// dq.pop_back_exn() /// ``` -pub fn pop_back_ignore[T](self : Deque[T]) -> Unit { +pub fn pop_back_exn[T](self : Deque[T]) -> Unit { if self.len == 0 { abort("The deque is empty!") } @@ -347,8 +349,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)? From 84a2e99527917e96ba2b2a6217c2495b617c629f Mon Sep 17 00:00:00 2001 From: Lampese Date: Wed, 20 Mar 2024 22:53:38 +0800 Subject: [PATCH 19/23] fix: bound check --- deque/deque.mbt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/deque/deque.mbt b/deque/deque.mbt index ef6800b0e..44b0fdb3c 100644 --- a/deque/deque.mbt +++ b/deque/deque.mbt @@ -260,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] @@ -295,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 From 9f3db42154c885bbf97131cdb1f093fc6294fb91 Mon Sep 17 00:00:00 2001 From: Lampese Date: Thu, 21 Mar 2024 01:40:55 +0800 Subject: [PATCH 20/23] feat: new functions to array and comment completion --- array/array.mbt | 235 ++++++++++++++++++++++++++++++++++++++++++++++-- vec/vec.mbt | 14 +++ 2 files changed, 244 insertions(+), 5 deletions(-) diff --git a/array/array.mbt b/array/array.mbt index a72cdbbc2..3a91c257d 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 trun. +/// +/// # 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] +/// 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]) == false { + 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]) == true { + 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/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) From 46fba928861f175a76e581c8dc3d6b70440576b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=9C=E7=81=AF?= Date: Thu, 21 Mar 2024 01:49:01 +0800 Subject: [PATCH 21/23] fix: typo --- array/array.mbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/array/array.mbt b/array/array.mbt index 3a91c257d..745be7805 100644 --- a/array/array.mbt +++ b/array/array.mbt @@ -110,7 +110,7 @@ test "iter_rev" { @assertion.assert_false(failed)? } -/// Iterates over the array with index in reversed trun. +/// Iterates over the array with index in reversed turn. /// /// # Arguments /// From 679e5844f0034b4873979be5a154d43521c2fa0f Mon Sep 17 00:00:00 2001 From: CAIMEOX <1853884864@qq.com> Date: Thu, 21 Mar 2024 09:39:04 +0800 Subject: [PATCH 22/23] fix: doc error style: update conditions --- array/array.mbt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/array/array.mbt b/array/array.mbt index 745be7805..683ba0da4 100644 --- a/array/array.mbt +++ b/array/array.mbt @@ -302,7 +302,7 @@ test "fold_rev" { /// # Example /// ``` /// let arr = [1, 2, 3, 4, 5] -/// arr // [5, 4, 3, 2, 1] +/// reverse(arr) // [5, 4, 3, 2, 1] /// ``` pub fn reverse[T](self : Array[T]) -> Unit { let mid_len = self.length() / 2 @@ -360,7 +360,7 @@ test "swap" { /// ``` pub fn all[T](self : Array[T], f : (T) -> Bool) -> Bool { for i = 0; i < self.length(); i = i + 1 { - if f(self[i]) == false { + if f(self[i]).not() { return false } } @@ -384,7 +384,7 @@ test "all" { /// ``` pub fn any[T](self : Array[T], f : (T) -> Bool) -> Bool { for i = 0; i < self.length(); i = i + 1 { - if f(self[i]) == true { + if f(self[i]) { return true } } From 1f52680ba7d14e2941c72fc9b34c8c5d366377be Mon Sep 17 00:00:00 2001 From: HongboZhang Date: Fri, 22 Mar 2024 09:52:48 +0800 Subject: [PATCH 23/23] remove unused bounds --- map/map.mbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) => {