From b75df315c6b71f7eea7185d93172ce792be09906 Mon Sep 17 00:00:00 2001 From: Hongbo Zhang Date: Wed, 15 May 2024 19:25:00 +0800 Subject: [PATCH 1/2] split map into multiple files --- map/map.mbt | 479 ---------------------------------------------- map/map_test.mbt | 277 +++++++++++++++++++++++++++ map/moon.pkg.json | 3 + map/types.mbt | 36 ++++ map/utils.mbt | 205 ++++++++++++++++++++ 5 files changed, 521 insertions(+), 479 deletions(-) create mode 100644 map/map_test.mbt create mode 100644 map/types.mbt create mode 100644 map/utils.mbt diff --git a/map/map.mbt b/map/map.mbt index 3469df5dd..027448fec 100644 --- a/map/map.mbt +++ b/map/map.mbt @@ -12,74 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -// -// An efficient implementation of Map. -// Based on size balanced binary trees, described by: -// Stephen Adams, "Efficient sets: a balancing act" Journal of Functional Programming 3(4):553-562, October 1993 -// - -/// Immuatable map, consists of key-value pairs. -/// -/// # Example -/// -/// ``` -/// let map1 = Map::[(3, "three"), (8, "eight"), (1, "one")] -/// let map2 = map1.insert(2, "two").remove(3) -/// println(map3.lookup(3)) // output: None -/// println(map3.lookup(2)) // output: Some("two") -/// let map3 = map2.insert(2, "updated") -/// println(map3.lookup(2)) // output: Some("updated") -/// ``` -enum Map[K, V] { - Empty - Tree(K, V, Int, Map[K, V], Map[K, V]) -} - -/// Create an empty map. -pub fn empty[K, V]() -> Map[K, V] { - Empty -} - -/// Create a map with a single key-value pair. -pub fn singleton[K, V](key : K, value : V) -> Map[K, V] { - Tree(key, value, 1, Empty, Empty) -} - -/// Check if the map contains a key. -/// O(log n). -pub fn member[K : Compare, V](self : Map[K, V], key : K) -> Bool { - loop self { - Empty => false - Tree(k, _, _, l, r) => { - let c = key.compare(k) - if c == 0 { - true - } else if c < 0 { - continue l - } else { - continue r - } - } - } -} - -/// Get the number of key-value pairs in the map. -pub fn size[K, V](self : Map[K, V]) -> Int { - match self { - Empty => 0 - Tree(_, _, n, _, _) => n - } -} - -pub fn is_empty[K, V](self : Map[K, V]) -> Bool { - self.size() == 0 -} - -fn new[K, V](key : K, value : V, l : Map[K, V], r : Map[K, V]) -> Map[K, V] { - let size = size(l) + size(r) + 1 - Tree(key, value, size, l, r) -} - /// Create a new map with a key-value pair inserted. /// O(log n). pub fn insert[K : Compare, V]( @@ -153,109 +85,6 @@ pub fn remove[K : Compare, V](self : Map[K, V], key : K) -> Map[K, V] { } } -/// Get the value associated with a key. -/// O(log n). -pub fn lookup[K : Compare, V](self : Map[K, V], key : K) -> Option[V] { - loop self { - Empty => None - Tree(k, v, _, l, r) => { - let c = key.compare(k) - if c == 0 { - Some(v) - } else if c < 0 { - continue l - } else { - continue r - } - } - } -} - -/// Iterate over the key-value pairs in the map. -pub fn iter[K, V](self : Map[K, V], f : (K, V) -> Unit) -> Unit { - match self { - Empty => () - Tree(k, v, _, l, r) => { - iter(l, f) - f(k, v) - iter(r, f) - } - } -} - -/// Iterate over the key-value pairs with index. -pub fn iteri[K, V](self : Map[K, V], f : (Int, K, V) -> Unit) -> Unit { - fn do_iteri(m : Map[K, V], f, i) { - match m { - Empty => () - Tree(k, v, _, l, r) => { - do_iteri(l, f, i) - f(size(l) + i, k, v) - do_iteri(r, f, size(l) + i + 1) - } - } - } - - do_iteri(self, f, 0) -} - -/// Maps over the values in the map. -pub fn map[K, X, Y](self : Map[K, X], f : (X) -> Y) -> Map[K, Y] { - match self { - Empty => Empty - Tree(k, v, s, l, r) => Tree(k, f(v), s, map(l, f), map(r, f)) - } -} - -/// Maps over the key-value pairs in the map. -pub fn map_with_key[K, X, Y](self : Map[K, X], f : (K, X) -> Y) -> Map[K, Y] { - match self { - Empty => Empty - Tree(k, v, s, l, r) => - Tree(k, f(k, v), s, map_with_key(l, f), map_with_key(r, f)) - } -} - -/// Fold the values in the map. -/// O(n). -pub fn fold[K, V, T](self : Map[K, V], f : (T, V) -> T, ~init : T) -> T { - foldl_with_key(self, fn(acc, _k, v) { f(acc, v) }, ~init) -} - -/// Post-order fold. -/// O(n). -pub fn foldr_with_key[K, V, T]( - self : Map[K, V], - f : (T, K, V) -> T, - ~init : T -) -> T { - fn go(m : Map[K, V], acc) { - match m { - Empty => acc - Tree(k, v, _, l, r) => go(l, f(go(r, acc), k, v)) - } - } - - go(self, init) -} - -/// Pre-order fold. -/// O(n). -pub fn foldl_with_key[K, V, T]( - self : Map[K, V], - f : (T, K, V) -> T, - ~init : T -) -> T { - fn go(m : Map[K, V], acc) { - match m { - Empty => acc - Tree(k, v, _, l, r) => go(r, f(go(l, acc), k, v)) - } - } - - go(self, init) -} - /// Filter values that satisfy the predicate pub fn filter[K : Compare, V]( self : Map[K, V], @@ -323,28 +152,6 @@ pub fn op_equal[K : Eq, V : Eq](self : Map[K, V], other : Map[K, V]) -> Bool { self.to_list() == other.to_list() } -pub fn debug_write[K : Debug, V : Debug]( - self : Map[K, V], - buffer : Buffer -) -> Unit { - buffer.write_string("Map::[") - let mut first = true - self.iter( - fn(k, v) { - if not(first) { - buffer.write_string(", ") - } - first = false - buffer.write_string("(") - k.debug_write(buffer) - buffer.write_string(", ") - v.debug_write(buffer) - buffer.write_string(")") - }, - ) - buffer.write_string("]") -} - /// The ratio between the sizes of the left and right subtrees. let ratio = 5 @@ -429,289 +236,3 @@ fn balance[K : Compare, V]( new(key, value, l, r) } } - -fn debug_tree[K : Show, V : Show](self : Map[K, V]) -> String { - match self { - Empty => "_" - Tree(k, v, _, l, r) => { - let l = debug_tree(l) - let r = debug_tree(r) - "(\(k),\(v),\(l),\(r))" - } - } -} - -/// Build a map from an array of key-value pairs. -/// O(n*log n). -pub fn Map::from_array[K : Compare, V](array : Array[(K, V)]) -> Map[K, V] { - for i = 0, mp = Map::Empty; i < array.length(); { - let (k, v) = array[i] - continue i + 1, mp.insert(k, v) - } else { - mp - } -} - -test "from_array" { - let m = Map::[(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")] - inspect( - m.debug_tree(), - content="(3,three,(1,one,(0,zero,_,_),(2,two,_,_)),(8,eight,_,_))", - )? -} - -test "insert" { - let m = Map::[(3, "three"), (8, "eight"), (1, "one")] - inspect(m.debug_tree(), content="(3,three,(1,one,_,_),(8,eight,_,_))")? - let m = m.insert(5, "five").insert(2, "two").insert(0, "zero").insert( - 1, "one_updated", - ) - inspect( - m.debug_tree(), - content="(3,three,(1,one_updated,(0,zero,_,_),(2,two,_,_)),(8,eight,(5,five,_,_),_))", - )? -} - -test "remove" { - let m = Map::[(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")] - inspect( - m.debug_tree(), - content="(3,three,(1,one,(0,zero,_,_),(2,two,_,_)),(8,eight,_,_))", - )? - let m = m.remove(1).remove(3) - @assertion.assert_eq(m.debug_tree(), "(2,two,(0,zero,_,_),(8,eight,_,_))")? -} - -test "lookup" { - let m = Map::[(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")] - @assertion.assert_eq(m.lookup(8), Some("eight"))? - @assertion.assert_eq(m.lookup(2), Some("two"))? - @assertion.assert_eq(m.lookup(4), None)? -} - -test "member" { - let m = Map::[(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")] - inspect( - m.debug_tree(), - content="(3,three,(1,one,(0,zero,_,_),(2,two,_,_)),(8,eight,_,_))", - )? - @assertion.assert_eq(m.member(8), true)? - @assertion.assert_eq(m.member(2), true)? - @assertion.assert_eq(m.member(4), false)? -} - -test "size" { - let m = Map::[(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")] - @assertion.assert_eq(m.size(), 5)? - let m = m.remove(1).remove(3) - @assertion.assert_eq(m.size(), 3)? -} - -test "is_empty" { - let m : Map[Int, Int] = empty() - @assertion.assert_eq(m.is_empty(), true)? - let m = m.insert(1, 1) - @assertion.assert_eq(m.is_empty(), false)? -} - -test "iter" { - let m = Map::[(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")] - let mut s = "" - m.iter(fn(k, v) { s = s + "(\(k),\(v))" }) - @assertion.assert_eq(s, "(0,zero)(1,one)(2,two)(3,three)(8,eight)")? -} - -test "iteri" { - let m = Map::[(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")] - let mut s = "" - m.iteri(fn(i, k, v) { s = s + "(\(i),\(k),\(v))" }) - @assertion.assert_eq(s, "(0,0,zero)(1,1,one)(2,2,two)(3,3,three)(4,8,eight)")? -} - -test "map" { - let m = Map::[(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")] - let n = m.map(fn(v) { v + "X" }) - @assertion.assert_eq( - n.debug_tree(), - "(3,threeX,(1,oneX,(0,zeroX,_,_),(2,twoX,_,_)),(8,eightX,_,_))", - )? -} - -test "map_with_key" { - let m = Map::[(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")] - let n = m.map_with_key(fn(k, v) { "\(k)-\(v)" }) - @assertion.assert_eq( - n.debug_tree(), - "(3,3-three,(1,1-one,(0,0-zero,_,_),(2,2-two,_,_)),(8,8-eight,_,_))", - )? -} - -test "fold" { - let m = Map::[("a", 1), ("b", 2), ("c", 3), ("d", 4), ("e", 5)] - @assertion.assert_eq(m.fold(fn(acc, v) { acc + v }, init=0), 15)? -} - -test "foldl_with_key" { - let m = Map::[("a", 1), ("b", 2), ("c", 3), ("d", 4), ("e", 5)] - @assertion.assert_eq( - m.foldl_with_key(fn(acc, k, v) { acc + k + v.to_string() }, init=""), - "a1b2c3d4e5", - )? -} - -test "foldr_with_key" { - let m = Map::[("a", 1), ("b", 2), ("c", 3), ("d", 4), ("e", 5)] - @assertion.assert_eq( - m.foldr_with_key(fn(acc, k, v) { acc + k + v.to_string() }, init=""), - "e5d4c3b2a1", - )? -} - -test "filter" { - let m = Map::[(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")] - let fm = m.filter(fn(v) { v.length() > 3 }) - @assertion.assert_eq(fm.debug_tree(), "(3,three,(0,zero,_,_),(8,eight,_,_))")? -} - -test "filter_with_key" { - let m = Map::[(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")] - let fm = m.filter_with_key(fn(k, v) { k > 3 && v.length() > 3 }) - @assertion.assert_eq(fm.debug_tree(), "(8,eight,_,_)")? -} - -test "to_list" { - let m = Map::[(1, "one"), (2, "two"), (3, "three")] - @assertion.assert_eq( - m.to_list(), - List::[(1, "one"), (2, "two"), (3, "three")], - )? -} - -test "to_asc_list" { - let m = Map::[(1, "one"), (2, "two"), (3, "three")] - @assertion.assert_eq( - m.to_asc_list(), - List::[(1, "one"), (2, "two"), (3, "three")], - )? -} - -test "to_desc_list" { - let m = Map::[(1, "one"), (2, "two"), (3, "three")] - @assertion.assert_eq( - m.to_desc_list(), - List::[(3, "three"), (2, "two"), (1, "one")], - )? -} - -test "to_vec" { - let m = Map::[(1, "one"), (2, "two"), (3, "three")] - let v = m.to_vec() - @assertion.assert_eq(v[0], (1, "one"))? - @assertion.assert_eq(v[1], (2, "two"))? - @assertion.assert_eq(v[2], (3, "three"))? -} - -test "keys" { - let m = Map::[(1, "one"), (2, "two"), (3, "three")] - @assertion.assert_eq(m.keys(), List::[1, 2, 3])? -} - -test "keys_set" { - let m = Map::[(1, "one"), (2, "two"), (3, "three")] - let ks = m.keys_set() - @assertion.assert_eq(ks.contain(1), true)? - @assertion.assert_eq(ks.contain(2), true)? - @assertion.assert_eq(ks.contain(3), true)? -} - -test "elems" { - let m = Map::[(1, "one"), (2, "two"), (3, "three")] - @assertion.assert_eq(m.elems(), List::["one", "two", "three"])? -} - -test "singleton" { - let m = singleton(3, "three") - @assertion.assert_eq(m.debug_tree(), "(3,three,_,_)")? -} - -test "empty" { - let m : Map[Int, Int] = empty() - @assertion.assert_eq(m.debug_tree(), "_")? -} - -test "split_max" { - let m = Map::[(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")] - @assertion.assert_eq( - m.debug_tree(), - "(3,three,(1,one,(0,zero,_,_),(2,two,_,_)),(8,eight,_,_))", - )? - let (k, v, r) = m.split_max() - @assertion.assert_eq(k, 8)? - @assertion.assert_eq(v, "eight")? - @assertion.assert_eq( - r.debug_tree(), - "(2,two,(1,one,(0,zero,_,_),_),(3,three,_,_))", - )? -} - -test "split_min" { - let m = Map::[(3, "three"), (8, "eight"), (2, "two"), (1, "one"), (0, "zero")] - @assertion.assert_eq( - m.debug_tree(), - "(3,three,(1,one,(0,zero,_,_),(2,two,_,_)),(8,eight,_,_))", - )? - let (k, v, r) = m.split_min() - @assertion.assert_eq(k, 0)? - @assertion.assert_eq(v, "zero")? - @assertion.assert_eq( - r.debug_tree(), - "(3,three,(1,one,_,(2,two,_,_)),(8,eight,_,_))", - )? -} - -test "glue" { - let m = Map::[(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")] - @assertion.assert_eq( - m.debug_tree(), - "(3,three,(1,one,(0,zero,_,_),(2,two,_,_)),(8,eight,_,_))", - )? - let (l, r) = match m { - Tree(_, _, _, l, r) => (l, r) - _ => abort("unreachable") - } - let m = l.glue(r) - @assertion.assert_eq( - m.debug_tree(), - "(2,two,(1,one,(0,zero,_,_),_),(8,eight,_,_))", - )? -} - -test "op_equal" { - let m1 = Map::[ - (3, "three"), - (8, "eight"), - (1, "one"), - (2, "two"), - (0, "zero"), - ] - let m2 = Map::[ - (3, "three"), - (8, "eight"), - (1, "one"), - (2, "two"), - (0, "zero"), - ] - let m3 = Map::[(3, "three"), (8, "eight"), (1, "one"), (2, "two")] - @assertion.assert_eq(m1 == m2, true)? - @assertion.assert_eq(m1 == m3, false)? -} - -test "debug_write" { - let m = Map::[(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")] - let buffer = Buffer::make(0) - m.debug_write(buffer) - @assertion.assert_eq( - buffer.to_string(), - "Map::[(0, \"zero\"), (1, \"one\"), (2, \"two\"), (3, \"three\"), (8, \"eight\")]", - )? -} diff --git a/map/map_test.mbt b/map/map_test.mbt new file mode 100644 index 000000000..aa6de9498 --- /dev/null +++ b/map/map_test.mbt @@ -0,0 +1,277 @@ +// 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. + +test "from_array" { + let m = Map::[(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")] + inspect( + m.debug_tree(), + content="(3,three,(1,one,(0,zero,_,_),(2,two,_,_)),(8,eight,_,_))", + )? +} + +test "insert" { + let m = Map::[(3, "three"), (8, "eight"), (1, "one")] + inspect(m.debug_tree(), content="(3,three,(1,one,_,_),(8,eight,_,_))")? + let m = m.insert(5, "five").insert(2, "two").insert(0, "zero").insert( + 1, "one_updated", + ) + inspect( + m.debug_tree(), + content="(3,three,(1,one_updated,(0,zero,_,_),(2,two,_,_)),(8,eight,(5,five,_,_),_))", + )? +} + +test "remove" { + let m = Map::[(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")] + inspect( + m.debug_tree(), + content="(3,three,(1,one,(0,zero,_,_),(2,two,_,_)),(8,eight,_,_))", + )? + let m = m.remove(1).remove(3) + @assertion.assert_eq(m.debug_tree(), "(2,two,(0,zero,_,_),(8,eight,_,_))")? +} + +test "lookup" { + let m = Map::[(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")] + @assertion.assert_eq(m.lookup(8), Some("eight"))? + @assertion.assert_eq(m.lookup(2), Some("two"))? + @assertion.assert_eq(m.lookup(4), None)? +} + +test "member" { + let m = Map::[(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")] + inspect( + m.debug_tree(), + content="(3,three,(1,one,(0,zero,_,_),(2,two,_,_)),(8,eight,_,_))", + )? + @assertion.assert_eq(m.member(8), true)? + @assertion.assert_eq(m.member(2), true)? + @assertion.assert_eq(m.member(4), false)? +} + +test "size" { + let m = Map::[(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")] + @assertion.assert_eq(m.size(), 5)? + let m = m.remove(1).remove(3) + @assertion.assert_eq(m.size(), 3)? +} + +test "is_empty" { + let m : Map[Int, Int] = empty() + @assertion.assert_eq(m.is_empty(), true)? + let m = m.insert(1, 1) + @assertion.assert_eq(m.is_empty(), false)? +} + +test "iter" { + let m = Map::[(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")] + let mut s = "" + m.iter(fn(k, v) { s = s + "(\(k),\(v))" }) + @assertion.assert_eq(s, "(0,zero)(1,one)(2,two)(3,three)(8,eight)")? +} + +test "iteri" { + let m = Map::[(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")] + let mut s = "" + m.iteri(fn(i, k, v) { s = s + "(\(i),\(k),\(v))" }) + @assertion.assert_eq(s, "(0,0,zero)(1,1,one)(2,2,two)(3,3,three)(4,8,eight)")? +} + +test "map" { + let m = Map::[(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")] + let n = m.map(fn(v) { v + "X" }) + @assertion.assert_eq( + n.debug_tree(), + "(3,threeX,(1,oneX,(0,zeroX,_,_),(2,twoX,_,_)),(8,eightX,_,_))", + )? +} + +test "map_with_key" { + let m = Map::[(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")] + let n = m.map_with_key(fn(k, v) { "\(k)-\(v)" }) + @assertion.assert_eq( + n.debug_tree(), + "(3,3-three,(1,1-one,(0,0-zero,_,_),(2,2-two,_,_)),(8,8-eight,_,_))", + )? +} + +test "fold" { + let m = Map::[("a", 1), ("b", 2), ("c", 3), ("d", 4), ("e", 5)] + @assertion.assert_eq(m.fold(fn(acc, v) { acc + v }, init=0), 15)? +} + +test "foldl_with_key" { + let m = Map::[("a", 1), ("b", 2), ("c", 3), ("d", 4), ("e", 5)] + @assertion.assert_eq( + m.foldl_with_key(fn(acc, k, v) { acc + k + v.to_string() }, init=""), + "a1b2c3d4e5", + )? +} + +test "foldr_with_key" { + let m = Map::[("a", 1), ("b", 2), ("c", 3), ("d", 4), ("e", 5)] + @assertion.assert_eq( + m.foldr_with_key(fn(acc, k, v) { acc + k + v.to_string() }, init=""), + "e5d4c3b2a1", + )? +} + +test "filter" { + let m = Map::[(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")] + let fm = m.filter(fn(v) { v.length() > 3 }) + @assertion.assert_eq(fm.debug_tree(), "(3,three,(0,zero,_,_),(8,eight,_,_))")? +} + +test "filter_with_key" { + let m = Map::[(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")] + let fm = m.filter_with_key(fn(k, v) { k > 3 && v.length() > 3 }) + @assertion.assert_eq(fm.debug_tree(), "(8,eight,_,_)")? +} + +test "to_list" { + let m = Map::[(1, "one"), (2, "two"), (3, "three")] + @assertion.assert_eq( + m.to_list(), + List::[(1, "one"), (2, "two"), (3, "three")], + )? +} + +test "to_asc_list" { + let m = Map::[(1, "one"), (2, "two"), (3, "three")] + @assertion.assert_eq( + m.to_asc_list(), + List::[(1, "one"), (2, "two"), (3, "three")], + )? +} + +test "to_desc_list" { + let m = Map::[(1, "one"), (2, "two"), (3, "three")] + @assertion.assert_eq( + m.to_desc_list(), + List::[(3, "three"), (2, "two"), (1, "one")], + )? +} + +test "to_vec" { + let m = Map::[(1, "one"), (2, "two"), (3, "three")] + let v = m.to_vec() + @assertion.assert_eq(v[0], (1, "one"))? + @assertion.assert_eq(v[1], (2, "two"))? + @assertion.assert_eq(v[2], (3, "three"))? +} + +test "keys" { + let m = Map::[(1, "one"), (2, "two"), (3, "three")] + @assertion.assert_eq(m.keys(), List::[1, 2, 3])? +} + +test "keys_set" { + let m = Map::[(1, "one"), (2, "two"), (3, "three")] + let ks = m.keys_set() + @assertion.assert_eq(ks.contain(1), true)? + @assertion.assert_eq(ks.contain(2), true)? + @assertion.assert_eq(ks.contain(3), true)? +} + +test "elems" { + let m = Map::[(1, "one"), (2, "two"), (3, "three")] + @assertion.assert_eq(m.elems(), List::["one", "two", "three"])? +} + +test "singleton" { + let m = singleton(3, "three") + @assertion.assert_eq(m.debug_tree(), "(3,three,_,_)")? +} + +test "empty" { + let m : Map[Int, Int] = empty() + @assertion.assert_eq(m.debug_tree(), "_")? +} + +test "split_max" { + let m = Map::[(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")] + @assertion.assert_eq( + m.debug_tree(), + "(3,three,(1,one,(0,zero,_,_),(2,two,_,_)),(8,eight,_,_))", + )? + let (k, v, r) = m.split_max() + @assertion.assert_eq(k, 8)? + @assertion.assert_eq(v, "eight")? + @assertion.assert_eq( + r.debug_tree(), + "(2,two,(1,one,(0,zero,_,_),_),(3,three,_,_))", + )? +} + +test "split_min" { + let m = Map::[(3, "three"), (8, "eight"), (2, "two"), (1, "one"), (0, "zero")] + @assertion.assert_eq( + m.debug_tree(), + "(3,three,(1,one,(0,zero,_,_),(2,two,_,_)),(8,eight,_,_))", + )? + let (k, v, r) = m.split_min() + @assertion.assert_eq(k, 0)? + @assertion.assert_eq(v, "zero")? + @assertion.assert_eq( + r.debug_tree(), + "(3,three,(1,one,_,(2,two,_,_)),(8,eight,_,_))", + )? +} + +test "glue" { + let m = Map::[(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")] + @assertion.assert_eq( + m.debug_tree(), + "(3,three,(1,one,(0,zero,_,_),(2,two,_,_)),(8,eight,_,_))", + )? + let (l, r) = match m { + Tree(_, _, _, l, r) => (l, r) + _ => abort("unreachable") + } + let m = l.glue(r) + @assertion.assert_eq( + m.debug_tree(), + "(2,two,(1,one,(0,zero,_,_),_),(8,eight,_,_))", + )? +} + +test "op_equal" { + let m1 = Map::[ + (3, "three"), + (8, "eight"), + (1, "one"), + (2, "two"), + (0, "zero"), + ] + let m2 = Map::[ + (3, "three"), + (8, "eight"), + (1, "one"), + (2, "two"), + (0, "zero"), + ] + let m3 = Map::[(3, "three"), (8, "eight"), (1, "one"), (2, "two")] + @assertion.assert_eq(m1 == m2, true)? + @assertion.assert_eq(m1 == m3, false)? +} + +test "debug_write" { + let m = Map::[(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")] + let buffer = Buffer::make(0) + m.debug_write(buffer) + @assertion.assert_eq( + buffer.to_string(), + "Map::[(0, \"zero\"), (1, \"one\"), (2, \"two\"), (3, \"three\"), (8, \"eight\")]", + )? +} diff --git a/map/moon.pkg.json b/map/moon.pkg.json index 74b6bdd31..e6bc5b6b1 100644 --- a/map/moon.pkg.json +++ b/map/moon.pkg.json @@ -8,5 +8,8 @@ "moonbitlang/core/vec", "moonbitlang/core/immutable_set", "moonbitlang/core/coverage" + ], + "test_import" : [ + ] } diff --git a/map/types.mbt b/map/types.mbt new file mode 100644 index 000000000..1617f1061 --- /dev/null +++ b/map/types.mbt @@ -0,0 +1,36 @@ +// 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. + +// +// An efficient implementation of Map. +// Based on size balanced binary trees, described by: +// Stephen Adams, "Efficient sets: a balancing act" Journal of Functional Programming 3(4):553-562, October 1993 +// + +/// Immuatable map, consists of key-value pairs. +/// +/// # Example +/// +/// ``` +/// let map1 = Map::[(3, "three"), (8, "eight"), (1, "one")] +/// let map2 = map1.insert(2, "two").remove(3) +/// println(map3.lookup(3)) // output: None +/// println(map3.lookup(2)) // output: Some("two") +/// let map3 = map2.insert(2, "updated") +/// println(map3.lookup(2)) // output: Some("updated") +/// ``` +enum Map[K, V] { + Empty + Tree(K, V, Int, Map[K, V], Map[K, V]) +} diff --git a/map/utils.mbt b/map/utils.mbt new file mode 100644 index 000000000..599132e42 --- /dev/null +++ b/map/utils.mbt @@ -0,0 +1,205 @@ +// 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. + +/// Create an empty map. +pub fn empty[K, V]() -> Map[K, V] { + Empty +} + +/// Create a map with a single key-value pair. +pub fn singleton[K, V](key : K, value : V) -> Map[K, V] { + Tree(key, value, 1, Empty, Empty) +} + +/// Check if the map contains a key. +/// O(log n). +pub fn member[K : Compare, V](self : Map[K, V], key : K) -> Bool { + loop self { + Empty => false + Tree(k, _, _, l, r) => { + let c = key.compare(k) + if c == 0 { + true + } else if c < 0 { + continue l + } else { + continue r + } + } + } +} + +/// Get the number of key-value pairs in the map. +pub fn size[K, V](self : Map[K, V]) -> Int { + match self { + Empty => 0 + Tree(_, _, n, _, _) => n + } +} + +pub fn is_empty[K, V](self : Map[K, V]) -> Bool { + self.size() == 0 +} + +fn new[K, V](key : K, value : V, l : Map[K, V], r : Map[K, V]) -> Map[K, V] { + let size = size(l) + size(r) + 1 + Tree(key, value, size, l, r) +} + +/// Get the value associated with a key. +/// O(log n). +pub fn lookup[K : Compare, V](self : Map[K, V], key : K) -> Option[V] { + loop self { + Empty => None + Tree(k, v, _, l, r) => { + let c = key.compare(k) + if c == 0 { + Some(v) + } else if c < 0 { + continue l + } else { + continue r + } + } + } +} + +/// Iterate over the key-value pairs in the map. +pub fn iter[K, V](self : Map[K, V], f : (K, V) -> Unit) -> Unit { + match self { + Empty => () + Tree(k, v, _, l, r) => { + iter(l, f) + f(k, v) + iter(r, f) + } + } +} + +/// Iterate over the key-value pairs with index. +pub fn iteri[K, V](self : Map[K, V], f : (Int, K, V) -> Unit) -> Unit { + fn do_iteri(m : Map[K, V], f, i) { + match m { + Empty => () + Tree(k, v, _, l, r) => { + do_iteri(l, f, i) + f(size(l) + i, k, v) + do_iteri(r, f, size(l) + i + 1) + } + } + } + + do_iteri(self, f, 0) +} + +/// Maps over the values in the map. +pub fn map[K, X, Y](self : Map[K, X], f : (X) -> Y) -> Map[K, Y] { + match self { + Empty => Empty + Tree(k, v, s, l, r) => Tree(k, f(v), s, map(l, f), map(r, f)) + } +} + +/// Maps over the key-value pairs in the map. +pub fn map_with_key[K, X, Y](self : Map[K, X], f : (K, X) -> Y) -> Map[K, Y] { + match self { + Empty => Empty + Tree(k, v, s, l, r) => + Tree(k, f(k, v), s, map_with_key(l, f), map_with_key(r, f)) + } +} + +/// Fold the values in the map. +/// O(n). +pub fn fold[K, V, T](self : Map[K, V], f : (T, V) -> T, ~init : T) -> T { + foldl_with_key(self, fn(acc, _k, v) { f(acc, v) }, ~init) +} + +/// Post-order fold. +/// O(n). +pub fn foldr_with_key[K, V, T]( + self : Map[K, V], + f : (T, K, V) -> T, + ~init : T +) -> T { + fn go(m : Map[K, V], acc) { + match m { + Empty => acc + Tree(k, v, _, l, r) => go(l, f(go(r, acc), k, v)) + } + } + + go(self, init) +} + +/// Pre-order fold. +/// O(n). +pub fn foldl_with_key[K, V, T]( + self : Map[K, V], + f : (T, K, V) -> T, + ~init : T +) -> T { + fn go(m : Map[K, V], acc) { + match m { + Empty => acc + Tree(k, v, _, l, r) => go(r, f(go(l, acc), k, v)) + } + } + + go(self, init) +} + +pub fn debug_write[K : Debug, V : Debug]( + self : Map[K, V], + buffer : Buffer +) -> Unit { + buffer.write_string("Map::[") + let mut first = true + self.iter( + fn(k, v) { + if not(first) { + buffer.write_string(", ") + } + first = false + buffer.write_string("(") + k.debug_write(buffer) + buffer.write_string(", ") + v.debug_write(buffer) + buffer.write_string(")") + }, + ) + buffer.write_string("]") +} + +fn debug_tree[K : Show, V : Show](self : Map[K, V]) -> String { + match self { + Empty => "_" + Tree(k, v, _, l, r) => { + let l = debug_tree(l) + let r = debug_tree(r) + "(\(k),\(v),\(l),\(r))" + } + } +} + +/// Build a map from an array of key-value pairs. +/// O(n*log n). +pub fn Map::from_array[K : Compare, V](array : Array[(K, V)]) -> Map[K, V] { + for i = 0, mp = Map::Empty; i < array.length(); { + let (k, v) = array[i] + continue i + 1, mp.insert(k, v) + } else { + mp + } +} From 86d8f8d7ec2d8af5a9f6faa22e7840afb4696dfa Mon Sep 17 00:00:00 2001 From: Hongbo Zhang Date: Wed, 15 May 2024 19:33:06 +0800 Subject: [PATCH 2/2] use label in enum types --- map/map.mbt | 28 +++++++++++++++------------- map/map_test.mbt | 2 +- map/types.mbt | 2 +- map/utils.mbt | 35 ++++++++++++++++++----------------- 4 files changed, 35 insertions(+), 32 deletions(-) diff --git a/map/map.mbt b/map/map.mbt index 027448fec..330d2ca4e 100644 --- a/map/map.mbt +++ b/map/map.mbt @@ -21,7 +21,7 @@ pub fn insert[K : Compare, V]( ) -> Map[K, V] { match self { Empty => singleton(key, value) - Tree(k, v, _, l, r) => + Tree(k, value=v, l, r, ..) => match key.compare(k) { -1 => balance(k, v, insert(l, key, value), r) 1 => balance(k, v, l, insert(r, key, value)) @@ -32,8 +32,8 @@ pub fn insert[K : Compare, V]( fn split_max[K : Compare, V](self : Map[K, V]) -> (K, V, Map[K, V]) { match self { - Tree(k, v, _, l, Empty) => (k, v, l) - Tree(k, v, _, l, r) => { + Tree(k, value=v, l, Empty, ..) => (k, v, l) + Tree(k, value=v, l, r, ..) => { let (k1, v1, r) = split_max(r) (k1, v1, balance(k, v, l, r)) } @@ -43,8 +43,8 @@ fn split_max[K : Compare, V](self : Map[K, V]) -> (K, V, Map[K, V]) { fn split_min[K : Compare, V](self : Map[K, V]) -> (K, V, Map[K, V]) { match self { - Tree(k, v, _, Empty, r) => (k, v, r) - Tree(k, v, _, l, r) => { + Tree(k, value=v, Empty, r, ..) => (k, v, r) + Tree(k, value=v, l, r, ..) => { let (k1, v1, l) = split_min(l) (k1, v1, balance(k, v, l, r)) } @@ -72,7 +72,7 @@ fn glue[K : Compare, V](self : Map[K, V], other : Map[K, V]) -> Map[K, V] { pub fn remove[K : Compare, V](self : Map[K, V], key : K) -> Map[K, V] { match self { Empty => Empty - Tree(k, v, _, l, r) => { + Tree(k, value=v, l, r, ..) => { let c = key.compare(k) if c == 0 { glue(l, r) @@ -100,7 +100,7 @@ pub fn filter_with_key[K : Compare, V]( ) -> Map[K, V] { match self { Empty => Empty - Tree(k, v, _, l, r) => + Tree(k, value=v, l, r, ..) => if pred(k, v) { balance(k, v, filter_with_key(l, pred), filter_with_key(r, pred)) } else { @@ -167,12 +167,14 @@ fn balance[K : Compare, V]( // / \ / \ // y z x y fn single_l { - k1, v1, x, Map::Tree(k2, v2, _, y, z) => new(k2, v2, new(k1, v1, x, y), z) + k1, v1, x, Map::Tree(k2, value=v2, y, z, ..) => + new(k2, v2, new(k1, v1, x, y), z) _, _, _, _ => abort("single_l error") } fn single_r { - k2, v2, Map::Tree(k1, v1, _, x, y), z => new(k1, v1, x, new(k2, v2, y, z)) + k2, v2, Map::Tree(k1, value=v1, x, y, ..), z => + new(k1, v1, x, new(k2, v2, y, z)) _, _, _, _ => abort("single_r error") } @@ -184,7 +186,7 @@ fn balance[K : Compare, V]( // / \ // y1 y2 fn double_l { - k1, v1, x, Map::Tree(k3, v3, _, Tree(k2, v2, _, y1, y2), z) => + k1, v1, x, Map::Tree(k3, value=v3, Tree(k2, value=v2, y1, y2, ..), z, ..) => new(k2, v2, new(k1, v1, x, y1), new(k3, v3, y2, z)) _, _, _, _ => abort("double_l error") } @@ -197,7 +199,7 @@ fn balance[K : Compare, V]( // / \ // y1 y2 fn double_r { - k3, v3, Map::Tree(k1, v1, _, x, Tree(k2, v2, _, y1, y2)), z => + k3, v3, Map::Tree(k1, value=v1, x, Tree(k2, value=v2, y1, y2, ..), ..), z => new(k2, v2, new(k1, v1, x, y1), new(k3, v3, y2, z)) _, _, _, _ => abort("double_r error") } @@ -209,7 +211,7 @@ fn balance[K : Compare, V]( } else if rn > ratio * ln { // right is too big let (rl, rr) = match r { - Tree(_, _, _, rl, rr) => (rl, rr) + Tree(_, rl, rr, ..) => (rl, rr) Empty => abort("unreachable") } let rln = size(rl) @@ -222,7 +224,7 @@ fn balance[K : Compare, V]( } else if ln > ratio * rn { // left is too big let (ll, lr) = match l { - Tree(_, _, _, ll, lr) => (ll, lr) + Tree(_, ll, lr, ..) => (ll, lr) Empty => abort("unreachable") } let lln = size(ll) diff --git a/map/map_test.mbt b/map/map_test.mbt index aa6de9498..75f9e66a3 100644 --- a/map/map_test.mbt +++ b/map/map_test.mbt @@ -236,7 +236,7 @@ test "glue" { "(3,three,(1,one,(0,zero,_,_),(2,two,_,_)),(8,eight,_,_))", )? let (l, r) = match m { - Tree(_, _, _, l, r) => (l, r) + Tree(_, l, r, ..) => (l, r) _ => abort("unreachable") } let m = l.glue(r) diff --git a/map/types.mbt b/map/types.mbt index 1617f1061..0987aea2c 100644 --- a/map/types.mbt +++ b/map/types.mbt @@ -32,5 +32,5 @@ /// ``` enum Map[K, V] { Empty - Tree(K, V, Int, Map[K, V], Map[K, V]) + Tree(K, ~value : V, ~size : Int, Map[K, V], Map[K, V]) } diff --git a/map/utils.mbt b/map/utils.mbt index 599132e42..1979f513e 100644 --- a/map/utils.mbt +++ b/map/utils.mbt @@ -19,7 +19,7 @@ pub fn empty[K, V]() -> Map[K, V] { /// Create a map with a single key-value pair. pub fn singleton[K, V](key : K, value : V) -> Map[K, V] { - Tree(key, value, 1, Empty, Empty) + Tree(key, ~value, size=1, Empty, Empty) } /// Check if the map contains a key. @@ -27,7 +27,7 @@ pub fn singleton[K, V](key : K, value : V) -> Map[K, V] { pub fn member[K : Compare, V](self : Map[K, V], key : K) -> Bool { loop self { Empty => false - Tree(k, _, _, l, r) => { + Tree(k, l, r, ..) => { let c = key.compare(k) if c == 0 { true @@ -44,7 +44,7 @@ pub fn member[K : Compare, V](self : Map[K, V], key : K) -> Bool { pub fn size[K, V](self : Map[K, V]) -> Int { match self { Empty => 0 - Tree(_, _, n, _, _) => n + Tree(_) as t => t.size } } @@ -54,7 +54,7 @@ pub fn is_empty[K, V](self : Map[K, V]) -> Bool { fn new[K, V](key : K, value : V, l : Map[K, V], r : Map[K, V]) -> Map[K, V] { let size = size(l) + size(r) + 1 - Tree(key, value, size, l, r) + Tree(key, ~value, ~size, l, r) } /// Get the value associated with a key. @@ -62,10 +62,10 @@ fn new[K, V](key : K, value : V, l : Map[K, V], r : Map[K, V]) -> Map[K, V] { pub fn lookup[K : Compare, V](self : Map[K, V], key : K) -> Option[V] { loop self { Empty => None - Tree(k, v, _, l, r) => { + Tree(k, ~value, l, r, ..) => { let c = key.compare(k) if c == 0 { - Some(v) + Some(value) } else if c < 0 { continue l } else { @@ -79,9 +79,9 @@ pub fn lookup[K : Compare, V](self : Map[K, V], key : K) -> Option[V] { pub fn iter[K, V](self : Map[K, V], f : (K, V) -> Unit) -> Unit { match self { Empty => () - Tree(k, v, _, l, r) => { + Tree(k, ~value, l, r, ..) => { iter(l, f) - f(k, v) + f(k, value) iter(r, f) } } @@ -92,9 +92,9 @@ pub fn iteri[K, V](self : Map[K, V], f : (Int, K, V) -> Unit) -> Unit { fn do_iteri(m : Map[K, V], f, i) { match m { Empty => () - Tree(k, v, _, l, r) => { + Tree(k, ~value, l, r, ..) => { do_iteri(l, f, i) - f(size(l) + i, k, v) + f(size(l) + i, k, value) do_iteri(r, f, size(l) + i + 1) } } @@ -107,7 +107,8 @@ pub fn iteri[K, V](self : Map[K, V], f : (Int, K, V) -> Unit) -> Unit { pub fn map[K, X, Y](self : Map[K, X], f : (X) -> Y) -> Map[K, Y] { match self { Empty => Empty - Tree(k, v, s, l, r) => Tree(k, f(v), s, map(l, f), map(r, f)) + Tree(k, ~value, ~size, l, r) => + Tree(k, value=f(value), ~size, map(l, f), map(r, f)) } } @@ -115,8 +116,8 @@ pub fn map[K, X, Y](self : Map[K, X], f : (X) -> Y) -> Map[K, Y] { pub fn map_with_key[K, X, Y](self : Map[K, X], f : (K, X) -> Y) -> Map[K, Y] { match self { Empty => Empty - Tree(k, v, s, l, r) => - Tree(k, f(k, v), s, map_with_key(l, f), map_with_key(r, f)) + Tree(k, ~value, l, r, ~size) => + Tree(k, value=f(k, value), ~size, map_with_key(l, f), map_with_key(r, f)) } } @@ -136,7 +137,7 @@ pub fn foldr_with_key[K, V, T]( fn go(m : Map[K, V], acc) { match m { Empty => acc - Tree(k, v, _, l, r) => go(l, f(go(r, acc), k, v)) + Tree(k, ~value, l, r, ..) => go(l, f(go(r, acc), k, value)) } } @@ -153,7 +154,7 @@ pub fn foldl_with_key[K, V, T]( fn go(m : Map[K, V], acc) { match m { Empty => acc - Tree(k, v, _, l, r) => go(r, f(go(l, acc), k, v)) + Tree(k, ~value, l, r, ..) => go(r, f(go(l, acc), k, value)) } } @@ -185,10 +186,10 @@ pub fn debug_write[K : Debug, V : Debug]( fn debug_tree[K : Show, V : Show](self : Map[K, V]) -> String { match self { Empty => "_" - Tree(k, v, _, l, r) => { + Tree(k, ~value, l, r, ..) => { let l = debug_tree(l) let r = debug_tree(r) - "(\(k),\(v),\(l),\(r))" + "(\(k),\(value),\(l),\(r))" } } }