From 297568fe138419f9c60fd9919831a8a23964607c Mon Sep 17 00:00:00 2001 From: Flamme Shadow Date: Mon, 2 Dec 2024 00:00:13 +0800 Subject: [PATCH 01/10] feat: impl non-modifying public methods of Array for ArrayView --- array/view.mbt | 157 ++++++++++++++++++++++++++++++++++++++++++ builtin/arrayview.mbt | 26 +++++++ 2 files changed, 183 insertions(+) diff --git a/array/view.mbt b/array/view.mbt index 3ff6d0eba..c9c763c77 100644 --- a/array/view.mbt +++ b/array/view.mbt @@ -27,3 +27,160 @@ pub fn each[T](self : ArrayView[T], f : (T) -> Unit) -> Unit { f(self[i]) } } + +pub fn rev_each[T](self : ArrayView[T], f : (T) -> Unit) -> Unit { + let len = self.length() + for i in 0.. Unit) -> Unit { + for i, v in self { + f(i, v) + } +} + +pub fn rev_eachi[T](self : ArrayView[T], f : (Int, T) -> Unit) -> Unit { + let len = self.length() + for i in 0.. Bool { + guard self.length() == other.length() else { return false } + for i in 0.. Int { + let len_self = self.length() + let len_other = other.length() + if len_self < len_other { + -1 + } else if len_self > len_other { + 1 + } else { + for i in 0.. Bool) -> ArrayView[T] { + let arr = [] + for v in self { + if f(v) { + arr.push(v) + } + } + arr[:] +} + +pub fn all[T](self : ArrayView[T], f : (T) -> Bool) -> Bool { + for i in 0.. Bool { + for v in self { + if v == value { + break true + } + } else { + false + } +} + +pub fn starts_with[T : Eq](self : ArrayView[T], prefix : ArrayView[T]) -> Bool { + if prefix.length() > self.length() { + return false + } + for i in 0.. Bool { + if suffix.length() > self.length() { + return false + } + for i in 0.. Bool { + for i in 0.. self[i] { + break false + } + } else { + true + } +} + +pub fn search[T : Eq](self : ArrayView[T], value : T) -> Int? { + for i = 0; i < self.length(); i = i + 1 { + if self[i] == value { + return Some(i) + } + } + None +} + +pub fn search_by[T](self : ArrayView[T], f : (T) -> Bool) -> Int? { + for i, v in self { + if f(v) { + break Some(i) + } + } else { + None + } +} + +pub fn last[T](self : ArrayView[T]) -> T? { + match self { + [] => None + [.., last] => Some(last) + } +} + +pub fn map_option[A, B](self : ArrayView[A], f : (A) -> B?) -> ArrayView[B] { + let result = [] + self.each( + fn { + x => + match f(x) { + None => () + Some(x) => result.push(x) + } + }, + ) + result[:] +} diff --git a/builtin/arrayview.mbt b/builtin/arrayview.mbt index 20b717ae6..b7096d47a 100644 --- a/builtin/arrayview.mbt +++ b/builtin/arrayview.mbt @@ -169,3 +169,29 @@ pub fn rev_foldi[A, B]( acc } } + +pub fn map[T, U](self : ArrayView[T], f : (T) -> U) -> ArrayView[U] { + if self.length() == 0 { + return [][:] + } + let arr = Array::make_uninit(self.length()) + for i, v in self { + arr.unsafe_set(i, f(v)) + } + arr[:] +} + +pub fn mapi[T, U](self : ArrayView[T], f : (Int, T) -> U) -> ArrayView[U] { + if self.length() == 0 { + return [][:] + } + let arr = Array::make_uninit(self.length()) + for i, v in self { + arr.unsafe_set(i, f(i, v)) + } + arr[:] +} + +pub fn is_empty[T](self : ArrayView[T]) -> Bool { + self.length() == 0 +} From 4715329d1b374761b0ca15b25a9192329d4fe11e Mon Sep 17 00:00:00 2001 From: Flamme Shadow Date: Mon, 2 Dec 2024 00:40:18 +0800 Subject: [PATCH 02/10] add more tests for ArrayView --- array/array.mbt | 4 -- array/array_test.mbt | 7 --- array/fixedarray_sort.mbt | 4 +- array/view.mbt | 6 +-- array/view_test.mbt | 109 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 114 insertions(+), 16 deletions(-) diff --git a/array/array.mbt b/array/array.mbt index 0f526842b..08ac24af7 100644 --- a/array/array.mbt +++ b/array/array.mbt @@ -87,10 +87,6 @@ pub fn Array::shuffle_in_place[T](arr : Array[T], rand~ : (Int) -> Int) -> Unit } } -test { - -} - ///| /// Shuffle the array using Knuth shuffle /// diff --git a/array/array_test.mbt b/array/array_test.mbt index efb0081bb..730ef7b3a 100644 --- a/array/array_test.mbt +++ b/array/array_test.mbt @@ -353,13 +353,6 @@ test "repeat" { assert_eq!(vv.length(), 4) } -test "flatten" { - let v = [[3, 4], [5, 6]] - let vv = v.flatten() - assert_eq!(vv, [3, 4, 5, 6]) - assert_eq!(vv.length(), 4) -} - test "fold" { let sum = [1, 2, 3, 4, 5].fold(init=0, fn { sum, elem => sum + elem }) assert_eq!(sum, 15) diff --git a/array/fixedarray_sort.mbt b/array/fixedarray_sort.mbt index 8c5633d55..5abdde5a3 100644 --- a/array/fixedarray_sort.mbt +++ b/array/fixedarray_sort.mbt @@ -523,9 +523,9 @@ test "stable_sort_complex" { index += 1 } } - assert_false!(is_sorted(arr)) + assert_false!(arr.is_sorted()) arr.stable_sort() - assert_true!(is_sorted(arr)) + assert_true!(arr.is_sorted()) } test "find_streak with empty array" { diff --git a/array/view.mbt b/array/view.mbt index c9c763c77..b7f9d84ed 100644 --- a/array/view.mbt +++ b/array/view.mbt @@ -49,7 +49,7 @@ pub fn rev_eachi[T](self : ArrayView[T], f : (Int, T) -> Unit) -> Unit { } ///| -/// Compares two arrayviews for equality. +/// Compares two arrayviews for equality of items, ignoring start/end positions. pub fn op_equal[T : Eq](self : ArrayView[T], other : ArrayView[T]) -> Bool { guard self.length() == other.length() else { return false } for i in 0.. Bool { if prefix.length() > self.length() { return false } - for i in 0.. Bool { } pub fn is_sorted[T : Compare](self : ArrayView[T]) -> Bool { - for i in 0.. self[i] { break false } diff --git a/array/view_test.mbt b/array/view_test.mbt index 9ae4bd6a4..debd956f9 100644 --- a/array/view_test.mbt +++ b/array/view_test.mbt @@ -27,3 +27,112 @@ test "each" { s.each(fn(x) { sum = sum + x }) assert_eq!(sum, 12) } + +test "map" { + let v = [1, 2, 3, 4, 5] + let s = v[2:5] + let s2 = s.map(fn(x) { x * 2 }) + assert_eq!(s2.length(), 3) + assert_eq!(s2, [6, 8, 10][:]) +} + +test "mapi" { + let v = [3, 4, 5] + let e = [][:] + inspect!(v.mapi(fn(i, x) { x + i }), content="[3, 5, 7]") + inspect!( + v.mapi(fn(i, x) { (x + i).to_string() }), + content= + #|["3", "5", "7"] + , + ) + inspect!(e.mapi(fn(_i, x) { x + 1 }), content="[]") +} + +test "filter" { + let arr = [1, 2, 3, 4, 5] + let slice = arr[1:5].filter(fn(x) { x % 2 == 0 }) + inspect!(slice, content="[2, 4]") +} + +test "all" { + let nums = [1, 2, 3, 5, 7, 9][:] + assert_eq!(nums.all(fn(x) { x % 2 == 0 }), false) +} + +test "op_equal" { + let v1 = [1, 2, 3][:] + let v2 = [1, 2, 3][:] + assert_true!(v1 == v2) + let v3 = [1, 2, 4, 6, 10][1:3] + let v4 = [0, 1, 2, 4, 6, 10, 12][2:4] + assert_true!(v3 == v4) + assert_false!(v1 == v3) +} + +test "contains" { + let v = [2, 3, 4, 5][:] + assert_true!(v.contains(2)) + assert_true!(v.contains(3)) + assert_true!(v.contains(4)) + assert_false!(v.contains(6)) +} + +test "starts_with" { + let v = [3, 4, 5][:] + assert_true!(v.starts_with([3, 4][:])) + assert_true!(v.starts_with([3, 4, 5][:])) + assert_false!(v.starts_with([3, 4, 6][:])) + assert_false!(v.starts_with([3, 4, 5, 6][:])) +} + +test "ends_with" { + let v = [3, 4, 5][:] + assert_true!(v.ends_with([4, 5][:])) + assert_true!(v.ends_with([3, 4, 5][:])) + assert_false!(v.ends_with([3, 4, 6][:])) + assert_false!(v.ends_with([2, 3, 4, 5][:])) +} + +test "search" { + let v = [3, 4, 5][:] + assert_eq!(v.search(3), Some(0)) + assert_eq!(v.search(4), Some(1)) + assert_eq!(v.search(5), Some(2)) + assert_eq!(v.search(6), None) +} + +test "fold" { + let sum = [1, 2, 3, 4, 5].fold(init=0, fn { sum, elem => sum + elem }) + assert_eq!(sum, 15) +} + +test "foldi" { + let sum = [1, 2, 3, 4, 5][:].foldi( + init=0, + fn { index, sum, _elem => sum + index }, + ) + assert_eq!(sum, 10) +} + +test "search_by" { + let arr = [1, 2, 3, 4, 5, 6, 7][:] + let index = arr.search_by(fn(x) { x == 3 }) + assert_eq!(index, Some(2)) +} + +test "is_empty" { + let v = [] + assert_true!(v.is_empty()) + v.push(3) + assert_false!(v.is_empty()) +} + +test "is_sorted" { + let v : ArrayView[Int] = [][:] + assert_true!(v.is_sorted()) + let v = [3, 4, 5][:] + assert_true!(v.is_sorted()) + let v2 = [3, 5, 4][:] + assert_false!(v2.is_sorted()) +} From bd8590a30e7c2e0bfbea3374519cc8f56ac1c0c4 Mon Sep 17 00:00:00 2001 From: Flamme Shadow Date: Mon, 2 Dec 2024 01:20:51 +0800 Subject: [PATCH 03/10] add more methods for ArrayView --- array/view.mbt | 71 ++++++++++++++++++++++++-------------- array/view_test.mbt | 80 +++++++++++++++++++++++++++++++++++++++++++ builtin/arrayview.mbt | 63 ++++++++++++++++++++++++++++++++++ 3 files changed, 188 insertions(+), 26 deletions(-) diff --git a/array/view.mbt b/array/view.mbt index b7f9d84ed..306d17e84 100644 --- a/array/view.mbt +++ b/array/view.mbt @@ -109,32 +109,6 @@ pub fn contains[T : Eq](self : ArrayView[T], value : T) -> Bool { } } -pub fn starts_with[T : Eq](self : ArrayView[T], prefix : ArrayView[T]) -> Bool { - if prefix.length() > self.length() { - return false - } - for i in 0.. Bool { - if suffix.length() > self.length() { - return false - } - for i in 0.. Bool { for i in 1.. self[i] { @@ -184,3 +158,48 @@ pub fn map_option[A, B](self : ArrayView[A], f : (A) -> B?) -> ArrayView[B] { ) result[:] } + +pub fn chunks[T](self : ArrayView[T], size : Int) -> ArrayView[ArrayView[T]] { + let chunks = [] + let mut i = 0 + while i < self.length() { + let chunk = Array::new(capacity=size) + for j = 0; j < size && i < self.length(); j = j + 1 { + chunk.push(self[i]) + i = i + 1 + } + chunks.push(chunk[:]) + } + chunks[:] +} + +pub fn chunk_by[T](self : ArrayView[T], pred : (T, T) -> Bool) -> ArrayView[ArrayView[T]] { + let chunks = [] + let mut i = 0 + while i < self.length() { + let chunk = [] + chunk.push(self[i]) + i = i + 1 + while i < self.length() && pred(self[i - 1], self[i]) { + chunk.push(self[i]) + i = i + 1 + } + chunks.push(chunk[:]) + } + chunks[:] +} + +pub fn split[T](self : ArrayView[T], pred : (T) -> Bool) -> ArrayView[ArrayView[T]] { + let chunks = [] + let mut i = 0 + while i < self.length() { + let chunk = [] + while i < self.length() && pred(self[i]).not() { + chunk.push(self[i]) + i = i + 1 + } + chunks.push(chunk[:]) + i = i + 1 + } + chunks[:] +} diff --git a/array/view_test.mbt b/array/view_test.mbt index debd956f9..8f8537c02 100644 --- a/array/view_test.mbt +++ b/array/view_test.mbt @@ -28,6 +28,30 @@ test "each" { assert_eq!(sum, 12) } +test "rev_each" { + let v = [3, 4, 5][:] + let mut sum = 0 + v.rev_each(fn(x) { sum = sum + x }) + assert_eq!(sum, 12) +} + +test "eachi" { + let v = [3, 4, 5][:] + let mut sum = 0 + for i, x in v { + // todo: maybe support (i,x) + sum = sum + x + i + } + assert_eq!(sum, 15) +} + +test "rev_eachi" { + let v = [3, 4, 5][:] + let mut sum = 0 + v.rev_eachi(fn(i, x) { sum = sum + x + i }) + assert_eq!(sum, 15) +} + test "map" { let v = [1, 2, 3, 4, 5] let s = v[2:5] @@ -102,6 +126,15 @@ test "search" { assert_eq!(v.search(6), None) } +test "rev" { + let v = [3, 4, 5][:] + let vv = v.rev() + assert_eq!(v, [3, 4, 5][:]) + assert_eq!(vv, [5, 4, 3][:]) + let vv2 = vv.rev() + assert_eq!(vv2, [3, 4, 5][:]) +} + test "fold" { let sum = [1, 2, 3, 4, 5].fold(init=0, fn { sum, elem => sum + elem }) assert_eq!(sum, 15) @@ -115,6 +148,30 @@ test "foldi" { assert_eq!(sum, 10) } +test "rev_fold" { + let sum = [1, 2, 3, 4, 5][:].rev_fold(init=0, fn { sum, elem => sum + elem }) + assert_eq!(sum, 15) + let op = fn { sum, elem => sum - elem } + let data = [1, 2, 3, 4, 5][:] + let ss0 = data.rev_fold(init=0, op) + let ss1 = data.rev().fold(init=0, op) + assert_eq!(ss0, ss1) +} + +test "rev_foldi" { + let sum = [1, 2, 3, 4, 5][:].rev_foldi( + init=0, + fn { index, sum, _elem => sum + index }, + ) + assert_eq!(sum, 10) + let op = fn { index, sum, elem => index - sum - elem } + let data = [1, 2, 3, 4, 5, 10] + let v0 = data.rev_foldi(init=0, op) + let v1 = data.rev().foldi(init=0, op) + assert_eq!(v0, v1) +} + + test "search_by" { let arr = [1, 2, 3, 4, 5, 6, 7][:] let index = arr.search_by(fn(x) { x == 3 }) @@ -136,3 +193,26 @@ test "is_sorted" { let v2 = [3, 5, 4][:] assert_false!(v2.is_sorted()) } + +test "chunks" { + let v = [1, 2, 3, 4, 5, 6, 7, 8, 9][:] + let chunks = v.chunks(3) + let chunks2 = v.chunks(2) + assert_eq!(chunks, [[1, 2, 3][:], [4, 5, 6][:], [7, 8, 9][:]][:]) + assert_eq!(chunks2, [[1, 2][:], [3, 4][:], [5, 6][:], [7, 8][:], [9][:]][:]) +} + +test "chunk_by" { + let v = [1, 1, 2, 3, 2, 3, 2, 3, 4][:] + let chunks = v.chunk_by(fn(x, y) { x <= y }) + assert_eq!(chunks[0], [1, 1, 2, 3][:]) + assert_eq!(chunks[1], [2, 3][:]) + assert_eq!(chunks[2], [2, 3, 4][:]) +} + +test "split" { + let v = [1, 0, 2, 0, 3, 0, 4][:] + let chunks = v.split(fn(x) { x == 0 }) + assert_eq!(chunks, [[1][:], [2][:], [3][:], [4][:]][:]) + assert_eq!(chunks.length(), 4) +} diff --git a/builtin/arrayview.mbt b/builtin/arrayview.mbt index b7096d47a..c5ef96026 100644 --- a/builtin/arrayview.mbt +++ b/builtin/arrayview.mbt @@ -195,3 +195,66 @@ pub fn mapi[T, U](self : ArrayView[T], f : (Int, T) -> U) -> ArrayView[U] { pub fn is_empty[T](self : ArrayView[T]) -> Bool { self.length() == 0 } + +pub fn starts_with[T : Eq](self : ArrayView[T], prefix : ArrayView[T]) -> Bool { + if prefix.length() > self.length() { + return false + } + for i in 0.. Bool { + if suffix.length() > self.length() { + return false + } + for i in 0.. ArrayView[T] { + let arr = [] + for i in 0.. (ArrayView[T], ArrayView[T]) { + if index < 0 || index > self.length() { + let len = self.length() + abort( + "index out of bounds: the len is from 0 to \{len} but the index is \{index}", + ) + } + (self[0:index], self[index:]) +} + +pub fn strip_prefix[T : Eq](self : ArrayView[T], prefix : ArrayView[T]) -> ArrayView[T]? { + if self.starts_with(prefix) { + let v = Array::make_uninit(self.length() - prefix.length())[:] + UninitializedArray::unsafe_blit( + v.buf, + 0, + self.buf, + prefix.length(), + self.length() - prefix.length(), + ) + Some(v) + } else { + None + } +} From da8f716684bf092c4c85e20d78e3ccc4b9aa4717 Mon Sep 17 00:00:00 2001 From: Flamme Shadow Date: Mon, 2 Dec 2024 01:27:43 +0800 Subject: [PATCH 04/10] add `strip_suffix` and tests for ArrayView --- array/view_test.mbt | 16 ++++++++++++++++ builtin/arrayview.mbt | 11 +++++++++++ 2 files changed, 27 insertions(+) diff --git a/array/view_test.mbt b/array/view_test.mbt index 8f8537c02..28636938e 100644 --- a/array/view_test.mbt +++ b/array/view_test.mbt @@ -216,3 +216,19 @@ test "split" { assert_eq!(chunks, [[1][:], [2][:], [3][:], [4][:]][:]) assert_eq!(chunks.length(), 4) } + +test "strip_prefix" { + let v = [3, 4, 5][:] + let sv = v.strip_prefix([3][:]) + assert_eq!(v, [3, 4, 5][:]) + assert_eq!(sv, Some([4, 5][:])) + assert_eq!(v.strip_prefix([4][:]), None) +} + +test "strip_suffix" { + let v = [3, 4, 5][:] + let sv = v.strip_suffix([5][:]) + assert_eq!(v, [3, 4, 5][:]) + assert_eq!(sv, Some([3, 4][:])) + assert_eq!(v.strip_suffix([4][:]), None) +} diff --git a/builtin/arrayview.mbt b/builtin/arrayview.mbt index c5ef96026..43edf727d 100644 --- a/builtin/arrayview.mbt +++ b/builtin/arrayview.mbt @@ -258,3 +258,14 @@ pub fn strip_prefix[T : Eq](self : ArrayView[T], prefix : ArrayView[T]) -> Array None } } + +pub fn strip_suffix[T : Eq](self : ArrayView[T], suffix : ArrayView[T]) -> ArrayView[T]? { + if self.ends_with(suffix) { + let v = Array::make_uninit(self.length() - suffix.length()) + let len = self.length() - suffix.length() + UninitializedArray::unsafe_blit(v.buffer(), 0, self.buf, 0, len) + Some(v[:]) + } else { + None + } +} From 7acef4d442bab899951d1aff9223385164a7fdae Mon Sep 17 00:00:00 2001 From: Flamme Shadow Date: Wed, 4 Dec 2024 02:16:37 +0800 Subject: [PATCH 05/10] add tests and methods for ArrayView --- array/array.mbti | 20 +++++++++++++++ array/view.mbt | 60 +++++++++++++++++++++++++++++++++++++++++++ array/view_test.mbt | 46 +++++++++++++++++++++++++++++++++ builtin/arrayview.mbt | 11 ++++++++ builtin/builtin.mbti | 10 ++++++++ 5 files changed, 147 insertions(+) diff --git a/array/array.mbti b/array/array.mbti index fbdf33c6e..536fd534e 100644 --- a/array/array.mbti +++ b/array/array.mbti @@ -63,8 +63,28 @@ impl Array { impl ArrayView { + all[T](Self[T], (T) -> Bool) -> Bool + binary_search[T : Compare](Self[T], T) -> Result[Int, Int] + binary_search_by[T](Self[T], (T) -> Int) -> Result[Int, Int] + chunk_by[T](Self[T], (T, T) -> Bool) -> Self[Self[T]] + chunks[T](Self[T], Int) -> Self[Self[T]] + compare[T : Compare](Self[T], Self[T]) -> Int + contains[T : Eq](Self[T], T) -> Bool each[T](Self[T], (T) -> Unit) -> Unit + eachi[T](Self[T], (Int, T) -> Unit) -> Unit + filter[T](Self[T], (T) -> Bool) -> Self[T] + is_sorted[T : Compare](Self[T]) -> Bool + last[T](Self[T]) -> T? + map_inplace[T](Self[T], (T) -> T) -> Unit + map_option[A, B](Self[A], (A) -> B?) -> Self[B] + mapi_inplace[T](Self[T], (Int, T) -> T) -> Unit + op_equal[T : Eq](Self[T], Self[T]) -> Bool + rev_each[T](Self[T], (T) -> Unit) -> Unit + rev_eachi[T](Self[T], (Int, T) -> Unit) -> Unit rev_inplace[T](Self[T]) -> Unit + search[T : Eq](Self[T], T) -> Int? + search_by[T](Self[T], (T) -> Bool) -> Int? + split[T](Self[T], (T) -> Bool) -> Self[Self[T]] } // Type aliases diff --git a/array/view.mbt b/array/view.mbt index 306d17e84..5e7ebe0f1 100644 --- a/array/view.mbt +++ b/array/view.mbt @@ -203,3 +203,63 @@ pub fn split[T](self : ArrayView[T], pred : (T) -> Bool) -> ArrayView[ArrayView[ } chunks[:] } + +pub fn map_inplace[T](self : ArrayView[T], f : (T) -> T) -> Unit { + for i, v in self { + self[i] = f(v) + } +} + +pub fn mapi_inplace[T](self : ArrayView[T], f : (Int, T) -> T) -> Unit { + for i, v in self { + self[i] = f(i, v) + } +} + +pub fn binary_search[T : Compare]( + self : ArrayView[T], + value : T +) -> Result[Int, Int] { + let len = self.length() + for i = 0, j = len; i < j; { + let h = i + (j - i) / 2 + // Note even if self[h] == value, we still continue the search + // because we want to find the leftmost match + if self.op_get(h) < value { + continue h + 1, j + } else { + continue i, h + } + } else { + if i < len && self.op_get(i) == value { + Ok(i) + } else { + Err(i) + } + } +} + +pub fn binary_search_by[T]( + self : ArrayView[T], + cmp : (T) -> Int +) -> Result[Int, Int] { + let len = self.length() + for i = 0, j = len; i < j; { + let h = i + (j - i) / 2 + // Note even if self[h] == value, we still continue the search + // because we want to find the leftmost match + if cmp(self.op_get(h)) < 0 { + continue h + 1, j + } else { + continue i, h + } + } else { + if i < len && cmp(self.op_get(i)) == 0 { + Ok(i) + } else { + Err(i) + } + } +} + + diff --git a/array/view_test.mbt b/array/view_test.mbt index 28636938e..1c09749e1 100644 --- a/array/view_test.mbt +++ b/array/view_test.mbt @@ -73,6 +73,26 @@ test "mapi" { inspect!(e.mapi(fn(_i, x) { x + 1 }), content="[]") } +test "map_inplace" { + let arr = [2, 3, 4, 5, 6] + let v = arr[1:4] + let e : Array[Int] = [] + v.map_inplace(fn(x) { x + 1 }) + e.map_inplace(fn(x) { x + 1 }) + inspect!(v, content="[4, 5, 6]") + inspect!(arr, content="[2, 4, 5, 6, 6]") + inspect!(e, content="[]") +} + +test "mapi_inplace" { + let v = [3, 4, 5] + let e : Array[Int] = [] + v.mapi_inplace(fn(i, x) { x + i }) + e.mapi_inplace(fn(_i, x) { x + 1 }) + inspect!(v, content="[3, 5, 7]") + inspect!(e, content="[]") +} + test "filter" { let arr = [1, 2, 3, 4, 5] let slice = arr[1:5].filter(fn(x) { x % 2 == 0 }) @@ -135,6 +155,24 @@ test "rev" { assert_eq!(vv2, [3, 4, 5][:]) } +test "rev_inplace" { + let arr = [1, 2, 3, 4, 5] + let v = arr[2:] + v.rev_inplace() + assert_eq!(v, [5, 4, 3][:]) + assert_eq!(arr, [1, 2, 5, 4, 3]) + v.rev_inplace() + assert_eq!(v, [3, 4, 5][:]) + assert_eq!(arr, [1, 2, 3, 4, 5]) + let v = [3, 4][:] + v.rev_inplace() + assert_eq!(v, [4, 3][:]) + v.rev_inplace() + assert_eq!(v, [3, 4][:]) + let empty : ArrayView[Int] = [][:] + assert_eq!(empty, empty) +} + test "fold" { let sum = [1, 2, 3, 4, 5].fold(init=0, fn { sum, elem => sum + elem }) assert_eq!(sum, 15) @@ -232,3 +270,11 @@ test "strip_suffix" { assert_eq!(sv, Some([3, 4][:])) assert_eq!(v.strip_suffix([4][:]), None) } + +test "copy" { + let arr = [1, 2, 3, 4, 5][:] + let copied = arr.copy() + assert_eq!(copied, [1, 2, 3, 4, 5][:]) + @test.is_not!(arr, copied) + inspect!(([][:] : ArrayView[Int]).copy(), content="[]") +} diff --git a/builtin/arrayview.mbt b/builtin/arrayview.mbt index 43edf727d..539bd8a18 100644 --- a/builtin/arrayview.mbt +++ b/builtin/arrayview.mbt @@ -269,3 +269,14 @@ pub fn strip_suffix[T : Eq](self : ArrayView[T], suffix : ArrayView[T]) -> Array None } } + +pub fn copy[T](self : ArrayView[T]) -> ArrayView[T] { + let len = self.length() + if len == 0 { + [][:] + } else { + let arr = Array::make(len, self[0]) + UninitializedArray::unsafe_blit(arr.buffer(), 0, self.buf, 0, len) + arr[:] + } +} diff --git a/builtin/builtin.mbti b/builtin/builtin.mbti index aedb5b418..fd61b048d 100644 --- a/builtin/builtin.mbti +++ b/builtin/builtin.mbti @@ -121,15 +121,25 @@ impl[X : Show] Show for Array[X] type ArrayView impl ArrayView { + copy[T](Self[T]) -> Self[T] + ends_with[T : Eq](Self[T], Self[T]) -> Bool fold[A, B](Self[A], init~ : B, (B, A) -> B) -> B foldi[A, B](Self[A], init~ : B, (Int, B, A) -> B) -> B + is_empty[T](Self[T]) -> Bool iter[A](Self[A]) -> Iter[A] length[T](Self[T]) -> Int + map[T, U](Self[T], (T) -> U) -> Self[U] + mapi[T, U](Self[T], (Int, T) -> U) -> Self[U] op_as_view[T](Self[T], start~ : Int, end? : Int) -> Self[T] op_get[T](Self[T], Int) -> T op_set[T](Self[T], Int, T) -> Unit + rev[T](Self[T]) -> Self[T] rev_fold[A, B](Self[A], init~ : B, (B, A) -> B) -> B rev_foldi[A, B](Self[A], init~ : B, (Int, B, A) -> B) -> B + split_at[T](Self[T], Int) -> (Self[T], Self[T]) + starts_with[T : Eq](Self[T], Self[T]) -> Bool + strip_prefix[T : Eq](Self[T], Self[T]) -> Self[T]? + strip_suffix[T : Eq](Self[T], Self[T]) -> Self[T]? swap[T](Self[T], Int, Int) -> Unit to_json[X : ToJson](Self[X]) -> Json to_string[X : Show](Self[X]) -> String From e5b434c6073093352712daa70f7ee5c0b8821a3f Mon Sep 17 00:00:00 2001 From: Wen Yuxiang Date: Thu, 5 Dec 2024 22:35:08 +0800 Subject: [PATCH 06/10] apply moon fmt --- array/view.mbt | 31 +++++++++++++++++++++++++++---- array/view_test.mbt | 1 - builtin/arrayview.mbt | 27 ++++++++++++++++++++++----- 3 files changed, 49 insertions(+), 10 deletions(-) diff --git a/array/view.mbt b/array/view.mbt index 5e7ebe0f1..dcfe91ebb 100644 --- a/array/view.mbt +++ b/array/view.mbt @@ -28,6 +28,7 @@ pub fn each[T](self : ArrayView[T], f : (T) -> Unit) -> Unit { } } +///| pub fn rev_each[T](self : ArrayView[T], f : (T) -> Unit) -> Unit { let len = self.length() for i in 0.. Unit) -> Unit { } } +///| pub fn eachi[T](self : ArrayView[T], f : (Int, T) -> Unit) -> Unit { for i, v in self { f(i, v) } } +///| pub fn rev_eachi[T](self : ArrayView[T], f : (Int, T) -> Unit) -> Unit { let len = self.length() for i in 0.. Bool { } } +///| pub fn compare[T : Compare](self : ArrayView[T], other : ArrayView[T]) -> Int { let len_self = self.length() let len_other = other.length() @@ -80,6 +84,7 @@ pub fn compare[T : Compare](self : ArrayView[T], other : ArrayView[T]) -> Int { } } +///| pub fn filter[T](self : ArrayView[T], f : (T) -> Bool) -> ArrayView[T] { let arr = [] for v in self { @@ -90,6 +95,7 @@ pub fn filter[T](self : ArrayView[T], f : (T) -> Bool) -> ArrayView[T] { arr[:] } +///| pub fn all[T](self : ArrayView[T], f : (T) -> Bool) -> Bool { for i in 0.. Bool) -> Bool { true } +///| pub fn contains[T : Eq](self : ArrayView[T], value : T) -> Bool { for v in self { if v == value { @@ -109,6 +116,7 @@ pub fn contains[T : Eq](self : ArrayView[T], value : T) -> Bool { } } +///| pub fn is_sorted[T : Compare](self : ArrayView[T]) -> Bool { for i in 1.. self[i] { @@ -119,6 +127,7 @@ pub fn is_sorted[T : Compare](self : ArrayView[T]) -> Bool { } } +///| pub fn search[T : Eq](self : ArrayView[T], value : T) -> Int? { for i = 0; i < self.length(); i = i + 1 { if self[i] == value { @@ -128,6 +137,7 @@ pub fn search[T : Eq](self : ArrayView[T], value : T) -> Int? { None } +///| pub fn search_by[T](self : ArrayView[T], f : (T) -> Bool) -> Int? { for i, v in self { if f(v) { @@ -138,6 +148,7 @@ pub fn search_by[T](self : ArrayView[T], f : (T) -> Bool) -> Int? { } } +///| pub fn last[T](self : ArrayView[T]) -> T? { match self { [] => None @@ -145,6 +156,7 @@ pub fn last[T](self : ArrayView[T]) -> T? { } } +///| pub fn map_option[A, B](self : ArrayView[A], f : (A) -> B?) -> ArrayView[B] { let result = [] self.each( @@ -159,6 +171,7 @@ pub fn map_option[A, B](self : ArrayView[A], f : (A) -> B?) -> ArrayView[B] { result[:] } +///| pub fn chunks[T](self : ArrayView[T], size : Int) -> ArrayView[ArrayView[T]] { let chunks = [] let mut i = 0 @@ -173,7 +186,11 @@ pub fn chunks[T](self : ArrayView[T], size : Int) -> ArrayView[ArrayView[T]] { chunks[:] } -pub fn chunk_by[T](self : ArrayView[T], pred : (T, T) -> Bool) -> ArrayView[ArrayView[T]] { +///| +pub fn chunk_by[T]( + self : ArrayView[T], + pred : (T, T) -> Bool +) -> ArrayView[ArrayView[T]] { let chunks = [] let mut i = 0 while i < self.length() { @@ -189,7 +206,11 @@ pub fn chunk_by[T](self : ArrayView[T], pred : (T, T) -> Bool) -> ArrayView[Arra chunks[:] } -pub fn split[T](self : ArrayView[T], pred : (T) -> Bool) -> ArrayView[ArrayView[T]] { +///| +pub fn split[T]( + self : ArrayView[T], + pred : (T) -> Bool +) -> ArrayView[ArrayView[T]] { let chunks = [] let mut i = 0 while i < self.length() { @@ -204,18 +225,21 @@ pub fn split[T](self : ArrayView[T], pred : (T) -> Bool) -> ArrayView[ArrayView[ chunks[:] } +///| pub fn map_inplace[T](self : ArrayView[T], f : (T) -> T) -> Unit { for i, v in self { self[i] = f(v) } } +///| pub fn mapi_inplace[T](self : ArrayView[T], f : (Int, T) -> T) -> Unit { for i, v in self { self[i] = f(i, v) } } +///| pub fn binary_search[T : Compare]( self : ArrayView[T], value : T @@ -239,6 +263,7 @@ pub fn binary_search[T : Compare]( } } +///| pub fn binary_search_by[T]( self : ArrayView[T], cmp : (T) -> Int @@ -261,5 +286,3 @@ pub fn binary_search_by[T]( } } } - - diff --git a/array/view_test.mbt b/array/view_test.mbt index 1c09749e1..91d0b7be7 100644 --- a/array/view_test.mbt +++ b/array/view_test.mbt @@ -209,7 +209,6 @@ test "rev_foldi" { assert_eq!(v0, v1) } - test "search_by" { let arr = [1, 2, 3, 4, 5, 6, 7][:] let index = arr.search_by(fn(x) { x == 3 }) diff --git a/builtin/arrayview.mbt b/builtin/arrayview.mbt index 539bd8a18..943d9592f 100644 --- a/builtin/arrayview.mbt +++ b/builtin/arrayview.mbt @@ -170,6 +170,7 @@ pub fn rev_foldi[A, B]( } } +///| pub fn map[T, U](self : ArrayView[T], f : (T) -> U) -> ArrayView[U] { if self.length() == 0 { return [][:] @@ -181,6 +182,7 @@ pub fn map[T, U](self : ArrayView[T], f : (T) -> U) -> ArrayView[U] { arr[:] } +///| pub fn mapi[T, U](self : ArrayView[T], f : (Int, T) -> U) -> ArrayView[U] { if self.length() == 0 { return [][:] @@ -192,10 +194,12 @@ pub fn mapi[T, U](self : ArrayView[T], f : (Int, T) -> U) -> ArrayView[U] { arr[:] } +///| pub fn is_empty[T](self : ArrayView[T]) -> Bool { self.length() == 0 } +///| pub fn starts_with[T : Eq](self : ArrayView[T], prefix : ArrayView[T]) -> Bool { if prefix.length() > self.length() { return false @@ -209,6 +213,7 @@ pub fn starts_with[T : Eq](self : ArrayView[T], prefix : ArrayView[T]) -> Bool { } } +///| pub fn ends_with[T : Eq](self : ArrayView[T], suffix : ArrayView[T]) -> Bool { if suffix.length() > self.length() { return false @@ -222,7 +227,7 @@ pub fn ends_with[T : Eq](self : ArrayView[T], suffix : ArrayView[T]) -> Bool { } } -/// TODO: perf could be optimized +///| TODO: perf could be optimized pub fn rev[T](self : ArrayView[T]) -> ArrayView[T] { let arr = [] for i in 0.. ArrayView[T] { arr[:] } -/// TODO: perf could be optimized +///| TODO: perf could be optimized /// @alert unsafe "Panic if index is out of bounds." -pub fn split_at[T](self : ArrayView[T], index : Int) -> (ArrayView[T], ArrayView[T]) { +pub fn split_at[T]( + self : ArrayView[T], + index : Int +) -> (ArrayView[T], ArrayView[T]) { if index < 0 || index > self.length() { let len = self.length() abort( @@ -243,7 +251,11 @@ pub fn split_at[T](self : ArrayView[T], index : Int) -> (ArrayView[T], ArrayView (self[0:index], self[index:]) } -pub fn strip_prefix[T : Eq](self : ArrayView[T], prefix : ArrayView[T]) -> ArrayView[T]? { +///| +pub fn strip_prefix[T : Eq]( + self : ArrayView[T], + prefix : ArrayView[T] +) -> ArrayView[T]? { if self.starts_with(prefix) { let v = Array::make_uninit(self.length() - prefix.length())[:] UninitializedArray::unsafe_blit( @@ -259,7 +271,11 @@ pub fn strip_prefix[T : Eq](self : ArrayView[T], prefix : ArrayView[T]) -> Array } } -pub fn strip_suffix[T : Eq](self : ArrayView[T], suffix : ArrayView[T]) -> ArrayView[T]? { +///| +pub fn strip_suffix[T : Eq]( + self : ArrayView[T], + suffix : ArrayView[T] +) -> ArrayView[T]? { if self.ends_with(suffix) { let v = Array::make_uninit(self.length() - suffix.length()) let len = self.length() - suffix.length() @@ -270,6 +286,7 @@ pub fn strip_suffix[T : Eq](self : ArrayView[T], suffix : ArrayView[T]) -> Array } } +///| pub fn copy[T](self : ArrayView[T]) -> ArrayView[T] { let len = self.length() if len == 0 { From 35da1e83aa61970cdd32a094b4becde4829f26f9 Mon Sep 17 00:00:00 2001 From: Flamme Shadow Date: Thu, 5 Dec 2024 23:33:54 +0800 Subject: [PATCH 07/10] impl flatten, sort, Eq/Compare traits for ArrayView --- array/sort.mbt | 6 ++++++ array/view.mbt | 13 +++++++++++-- array/view_test.mbt | 7 +++++++ builtin/arrayview_test.mbt | 37 +++++++++++++++++++++++++++++++++++++ 4 files changed, 61 insertions(+), 2 deletions(-) diff --git a/array/sort.mbt b/array/sort.mbt index 79d749e2e..467708e3a 100644 --- a/array/sort.mbt +++ b/array/sort.mbt @@ -29,6 +29,12 @@ pub fn sort[T : Compare](self : Array[T]) -> Unit { quick_sort(self[:len], None, get_limit(len)) } +///| +pub fn sort[T : Compare](self : ArrayView[T]) -> Unit { + let len = self.length() + quick_sort(self[:len], None, get_limit(len)) +} + ///| fn quick_sort[T : Compare](arr : ArrayView[T], pred : T?, limit : Int) -> Unit { let mut limit = limit diff --git a/array/view.mbt b/array/view.mbt index dcfe91ebb..3db97b460 100644 --- a/array/view.mbt +++ b/array/view.mbt @@ -53,7 +53,7 @@ pub fn rev_eachi[T](self : ArrayView[T], f : (Int, T) -> Unit) -> Unit { ///| /// Compares two arrayviews for equality of items, ignoring start/end positions. -pub fn op_equal[T : Eq](self : ArrayView[T], other : ArrayView[T]) -> Bool { +pub impl[T : Eq] Eq for ArrayView[T] with op_equal(self, other) { guard self.length() == other.length() else { return false } for i in 0.. Bool { } ///| -pub fn compare[T : Compare](self : ArrayView[T], other : ArrayView[T]) -> Int { +pub impl[T : Compare] Compare for ArrayView[T] with compare(self, other) -> Int { let len_self = self.length() let len_other = other.length() if len_self < len_other { @@ -286,3 +286,12 @@ pub fn binary_search_by[T]( } } } + +///| +pub fn flatten[T](self : ArrayView[ArrayView[T]]) -> ArrayView[T] { + let v = [] + for view in self { + v.push_iter(view.iter()) + } + v[:] +} diff --git a/array/view_test.mbt b/array/view_test.mbt index 91d0b7be7..6b4fdf4a9 100644 --- a/array/view_test.mbt +++ b/array/view_test.mbt @@ -277,3 +277,10 @@ test "copy" { @test.is_not!(arr, copied) inspect!(([][:] : ArrayView[Int]).copy(), content="[]") } + +test "flatten" { + let v = [[3, 4][:], [5, 6][:]][:] + let vv = v.flatten() + assert_eq!(vv, [3, 4, 5, 6][:]) + assert_eq!(vv.length(), 4) +} diff --git a/builtin/arrayview_test.mbt b/builtin/arrayview_test.mbt index 19a66b97e..f7d3150e4 100644 --- a/builtin/arrayview_test.mbt +++ b/builtin/arrayview_test.mbt @@ -78,3 +78,40 @@ test "arrayview_rev_foldi" { let sum = arr.rev_foldi(fn(i, acc, x) { acc + i + x }, init=0) inspect!(sum, content="9") } + +test "arrayview_binary_search_int_test" { + let arr = [1, 2, 3, 4][:] + assert_eq!(arr.binary_search(-100), Err(0)) + assert_eq!(arr.binary_search(-1), Err(0)) + assert_eq!(arr.binary_search(1), Ok(0)) + assert_eq!(arr.binary_search(3), Ok(2)) + assert_eq!(arr.binary_search(4), Ok(3)) + assert_eq!(arr.binary_search(5), Err(4)) + assert_eq!(arr.binary_search(60), Err(4)) +} + +test "arrayview_binary_search_duplicate_int_test" { + let arr = [1, 2, 3, 3, 4, 4, 4, 5][:] + assert_eq!(arr.binary_search(3), Ok(2)) + assert_eq!(arr.binary_search(4), Ok(4)) + assert_eq!(arr.binary_search(5), Ok(7)) + let arr = [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55] + assert_eq!(arr.binary_search(1), Ok(1)) +} + +test "arrayview_binary_search_float_test" { + let arr = [-0.25, 0.0, 1.5][:] + assert_eq!(arr.binary_search(-0.25), Ok(0)) + assert_eq!(arr.binary_search(0.0), Ok(1)) + assert_eq!(arr.binary_search(1.5), Ok(2)) + assert_eq!(arr.binary_search(1.6), Err(3)) +} + +test "arrayview_binary_search_str_test" { + let arr = ["hello", "world", "moon", "bit"][:] + arr.sort() + assert_eq!(arr.binary_search("bit"), Ok(0)) + assert_eq!(arr.binary_search("moon"), Ok(1)) + assert_eq!(arr.binary_search("hello"), Ok(2)) + assert_eq!(arr.binary_search("world"), Ok(3)) +} From f0c3d178c93d3fc667ba9b653fccdced7942ff53 Mon Sep 17 00:00:00 2001 From: Flamme Shadow Date: Thu, 5 Dec 2024 23:44:53 +0800 Subject: [PATCH 08/10] moon info --- array/array.mbti | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/array/array.mbti b/array/array.mbti index 536fd534e..031817156 100644 --- a/array/array.mbti +++ b/array/array.mbti @@ -68,22 +68,22 @@ impl ArrayView { binary_search_by[T](Self[T], (T) -> Int) -> Result[Int, Int] chunk_by[T](Self[T], (T, T) -> Bool) -> Self[Self[T]] chunks[T](Self[T], Int) -> Self[Self[T]] - compare[T : Compare](Self[T], Self[T]) -> Int contains[T : Eq](Self[T], T) -> Bool each[T](Self[T], (T) -> Unit) -> Unit eachi[T](Self[T], (Int, T) -> Unit) -> Unit filter[T](Self[T], (T) -> Bool) -> Self[T] + flatten[T](Self[Self[T]]) -> Self[T] is_sorted[T : Compare](Self[T]) -> Bool last[T](Self[T]) -> T? map_inplace[T](Self[T], (T) -> T) -> Unit map_option[A, B](Self[A], (A) -> B?) -> Self[B] mapi_inplace[T](Self[T], (Int, T) -> T) -> Unit - op_equal[T : Eq](Self[T], Self[T]) -> Bool rev_each[T](Self[T], (T) -> Unit) -> Unit rev_eachi[T](Self[T], (Int, T) -> Unit) -> Unit rev_inplace[T](Self[T]) -> Unit search[T : Eq](Self[T], T) -> Int? search_by[T](Self[T], (T) -> Bool) -> Int? + sort[T : Compare](Self[T]) -> Unit split[T](Self[T], (T) -> Bool) -> Self[Self[T]] } From 9aa755b54e8beaac159b2729d670a50cb3e9877c Mon Sep 17 00:00:00 2001 From: Flamme Shadow Date: Sat, 7 Dec 2024 12:24:17 +0800 Subject: [PATCH 09/10] impl repeat and fix copy for ArrayView --- array/view_test.mbt | 22 +++++++++++++++++++--- builtin/arrayview.mbt | 8 +++++++- builtin/builtin.mbti | 1 + 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/array/view_test.mbt b/array/view_test.mbt index 6b4fdf4a9..d8db0ce00 100644 --- a/array/view_test.mbt +++ b/array/view_test.mbt @@ -271,11 +271,15 @@ test "strip_suffix" { } test "copy" { - let arr = [1, 2, 3, 4, 5][:] - let copied = arr.copy() + let v1 = [1, 2, 3, 4, 5][:] + let copied = v1.copy() assert_eq!(copied, [1, 2, 3, 4, 5][:]) - @test.is_not!(arr, copied) + @test.is_not!(v1, copied) inspect!(([][:] : ArrayView[Int]).copy(), content="[]") + let v2 = [1, 2, 3, 4, 5][1:4] + let copied = v2.copy() + assert_eq!(copied, [2, 3, 4][:]) + @test.is_not!(v1, copied) } test "flatten" { @@ -283,4 +287,16 @@ test "flatten" { let vv = v.flatten() assert_eq!(vv, [3, 4, 5, 6][:]) assert_eq!(vv.length(), 4) + let v : ArrayView[ArrayView[Int]] = [][:] + assert_eq!(v.flatten(), [][:]) + let v = [[1, 2, 3, 4][:], [4, 5, 6, 7][1:4], [7, 8, 9, 10][1:3]][:] + let vv = v.flatten() + assert_eq!(vv, [1, 2, 3, 4, 5, 6, 7, 8, 9][:]) +} + +test "repeat" { + let v = [1, 2, 3, 4, 5][2:4] + let vv = v.repeat(2) + assert_eq!(vv, [3, 4, 3, 4][:]) + assert_eq!(vv.length(), 4) } diff --git a/builtin/arrayview.mbt b/builtin/arrayview.mbt index 943d9592f..b7384c5f5 100644 --- a/builtin/arrayview.mbt +++ b/builtin/arrayview.mbt @@ -293,7 +293,13 @@ pub fn copy[T](self : ArrayView[T]) -> ArrayView[T] { [][:] } else { let arr = Array::make(len, self[0]) - UninitializedArray::unsafe_blit(arr.buffer(), 0, self.buf, 0, len) + UninitializedArray::unsafe_blit(arr.buffer(), 0, self.buf, self.start, len) arr[:] } } + +pub fn repeat[T](self : ArrayView[T], times : Int) -> ArrayView[T] { + let v = Array::make_uninit(self.length()) + UninitializedArray::unsafe_blit(v.buffer(), 0, self.buf, self.start, self.length()) + v.repeat(times)[:] +} diff --git a/builtin/builtin.mbti b/builtin/builtin.mbti index fd61b048d..8705f2c50 100644 --- a/builtin/builtin.mbti +++ b/builtin/builtin.mbti @@ -133,6 +133,7 @@ impl ArrayView { op_as_view[T](Self[T], start~ : Int, end? : Int) -> Self[T] op_get[T](Self[T], Int) -> T op_set[T](Self[T], Int, T) -> Unit + repeat[T](Self[T], Int) -> Self[T] rev[T](Self[T]) -> Self[T] rev_fold[A, B](Self[A], init~ : B, (B, A) -> B) -> B rev_foldi[A, B](Self[A], init~ : B, (Int, B, A) -> B) -> B From cb3766ebb0db48b353deae6e9c92437bdcebccfd Mon Sep 17 00:00:00 2001 From: Flamme Shadow Date: Sat, 7 Dec 2024 12:43:35 +0800 Subject: [PATCH 10/10] add sort methods --- array/array.mbti | 2 ++ array/sort_by.mbt | 15 +++++++++++++++ array/view_test.mbt | 21 +++++++++++++++++++++ builtin/arrayview.mbt | 9 ++++++++- 4 files changed, 46 insertions(+), 1 deletion(-) diff --git a/array/array.mbti b/array/array.mbti index 031817156..e36f8a391 100644 --- a/array/array.mbti +++ b/array/array.mbti @@ -84,6 +84,8 @@ impl ArrayView { search[T : Eq](Self[T], T) -> Int? search_by[T](Self[T], (T) -> Bool) -> Int? sort[T : Compare](Self[T]) -> Unit + sort_by[T](Self[T], (T, T) -> Int) -> Unit + sort_by_key[T, K : Compare](Self[T], (T) -> K) -> Unit split[T](Self[T], (T) -> Bool) -> Self[Self[T]] } diff --git a/array/sort_by.mbt b/array/sort_by.mbt index 35d499405..82446031a 100644 --- a/array/sort_by.mbt +++ b/array/sort_by.mbt @@ -33,6 +33,16 @@ pub fn sort_by_key[T, K : Compare](self : Array[T], map : (T) -> K) -> Unit { ) } +///| +pub fn sort_by_key[T, K : Compare](self : ArrayView[T], map : (T) -> K) -> Unit { + quick_sort_by( + self, + fn(a, b) { map(a).compare(map(b)) }, + None, + get_limit(self.length()), + ) +} + ///| /// Sorts the array with a custom comparison function. /// @@ -49,6 +59,11 @@ pub fn sort_by[T](self : Array[T], cmp : (T, T) -> Int) -> Unit { quick_sort_by(self[:], cmp, None, get_limit(self.length())) } +///| +pub fn sort_by[T](self : ArrayView[T], cmp : (T, T) -> Int) -> Unit { + quick_sort_by(self[:], cmp, None, get_limit(self.length())) +} + ///| fn quick_sort_by[T]( arr : ArrayView[T], diff --git a/array/view_test.mbt b/array/view_test.mbt index d8db0ce00..3dad7483e 100644 --- a/array/view_test.mbt +++ b/array/view_test.mbt @@ -300,3 +300,24 @@ test "repeat" { assert_eq!(vv, [3, 4, 3, 4][:]) assert_eq!(vv.length(), 4) } + +test "sort" { + let v = [5, 4, 3, 2, 1][:] + v.sort() + assert_eq!(v, [1, 2, 3, 4, 5][:]) + let v = [5, 4, 3, 2, 1][1:4] + v.sort() + assert_eq!(v, [2, 3, 4][:]) +} + +test "sort_by" { + let v = [5, 3, 2, 4, 1][:] + v.sort_by(fn(a, b) { a - b }) + assert_eq!(v, [1, 2, 3, 4, 5][:]) +} + +test "sort_by_key" { + let v = [5, 3, 2, 4, 1][:] + v.sort_by_key(fn(x) { -x }) + assert_eq!(v, [5, 4, 3, 2, 1][:]) +} diff --git a/builtin/arrayview.mbt b/builtin/arrayview.mbt index b7384c5f5..1bafa80e0 100644 --- a/builtin/arrayview.mbt +++ b/builtin/arrayview.mbt @@ -298,8 +298,15 @@ pub fn copy[T](self : ArrayView[T]) -> ArrayView[T] { } } +///| pub fn repeat[T](self : ArrayView[T], times : Int) -> ArrayView[T] { let v = Array::make_uninit(self.length()) - UninitializedArray::unsafe_blit(v.buffer(), 0, self.buf, self.start, self.length()) + UninitializedArray::unsafe_blit( + v.buffer(), + 0, + self.buf, + self.start, + self.length(), + ) v.repeat(times)[:] }