Skip to content

Commit

Permalink
Buffer Class Extension (#417)
Browse files Browse the repository at this point in the history
Base Library Extension Doc: https://docs.google.com/document/d/1uKouujLL3KueLpqjjFV2gd4dqu_6GYD107WBb6_GSVE/edit

- [x] Complete unit tests
- [x] Consider making initCapacity an optional value with a default initial capacity
- [x] While loop iterations over Arrays may be faster with an iterator due to a compiler optimization
- [x] In functions where a new buffer is being constructed, have some consistent scheme for how to determine the new buffer's capacity. Should the new buffer have just enough space to hold its element, or be given some insertion leeway?
- [x] Some class methods can be moved outside of the class since they don't need access to the underlying array
- [x] Consider behavior of transpose on empty Buffers
- [x] sort() can be optimized by hoisting helper functions
- [x] Restore broken methods in Array.mo and Hash.mo
- [x] Compare with original Buffer class and try to keep some level of backwards compatibility / deprecate old functions
- [x] Fix naming to be consistent with user friendliness, other classes, and style / interface guide
- [x] Add user friendly error messages / error checking
- [x] Cleanup tests
- [x] Add documentation for each function
- [ ] Add examples for each function (put up as a separate PR)
- [ ] A primitive array tabulate for var arrays would allow some nice optimizations (put up as a separate PR)
- [x] Profile this class to see if a more scalable dynamic sequence is necessary and to resolve ambiguous design decisions


This last point is related to the RRB Trees, Rope, and Finger Trees data structures that I was discussing before. For various reasons, I am leaning towards an RRB Tree implementation of a scalable sequence structure, if it is necessary. See a talk on this data structure here: https://www.youtube.com/watch?v=sPhpelUfu8Q

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Kento Sugama <kento.sugama@dfinity.org>
  • Loading branch information
3 people committed Oct 21, 2022
1 parent 08507fc commit d52aecd
Show file tree
Hide file tree
Showing 6 changed files with 5,006 additions and 224 deletions.
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,9 @@
# Builds
_build
_out

# Macs
.DS_Store

# IDEs
.vscode/
88 changes: 66 additions & 22 deletions src/Array.mo
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
/// Functions on Arrays

import Buffer "Buffer";
import I "IterType";
import Option "Option";
import Order "Order";
Expand Down Expand Up @@ -42,7 +41,7 @@ module {
};
};

/// Sorts the given array according to the `cmp` function.
/// Sorts the given array, in ascending order, according to the `compare` function.
/// This is a _stable_ sort.
///
/// ```motoko
Expand All @@ -51,13 +50,13 @@ module {
/// let xs = [4, 2, 6];
/// assert(Array.sort(xs, Nat.compare) == [2, 4, 6])
/// ```
public func sort<A>(xs : [A], cmp : (A, A) -> Order.Order) : [A] {
public func sort<A>(xs : [A], compare : (A, A) -> Order.Order) : [A] {
let tmp : [var A] = thaw(xs);
sortInPlace(tmp, cmp);
sortInPlace(tmp, compare);
freeze(tmp)
};

/// Sorts the given array in place according to the `cmp` function.
/// Sorts the given array, in ascending order, in place, according to the `compare` function.
/// This is a _stable_ sort.
///
/// ```motoko
Expand All @@ -67,7 +66,7 @@ module {
/// Array.sortInPlace(xs, Nat.compare);
/// assert(Array.freeze(xs) == [1, 2, 4, 5, 6])
/// ```
public func sortInPlace<A>(xs : [var A], cmp : (A, A) -> Order.Order) {
public func sortInPlace<A>(xs : [var A], compare : (A, A) -> Order.Order) {
if (xs.size() < 2) return;
let aux : [var A] = tabulateVar<A>(xs.size(), func i { xs[i] });

Expand All @@ -87,7 +86,7 @@ module {
} else if (j > hi) {
xs[k] := aux[i];
i += 1;
} else if (Order.isLess(cmp(aux[j], aux[i]))) {
} else if (Order.isLess(compare(aux[j], aux[i]))) {
xs[k] := aux[j];
j += 1;
} else {
Expand Down Expand Up @@ -119,25 +118,70 @@ module {
};
/// Output array contains each array-value if and only if the predicate is true; ordering retained.
public func filter<A>(xs : [A], f : A -> Bool) : [A] {
let ys : Buffer.Buffer<A> = Buffer.Buffer(xs.size());
for (x in xs.vals()) {
if (f(x)) {
ys.add(x);
};
};
ys.toArray();
var count = 0;
let keep =
Prim.Array_tabulate<Bool>(
xs.size(),
func i {
if (f(xs[i])) {
count += 1;
true
} else {
false
}
}
);
var nextKeep = 0;
Prim.Array_tabulate<A>(
count,
func _ {
while (not keep[nextKeep]) {
nextKeep += 1;
};
nextKeep += 1;
xs[nextKeep - 1];
}
)
};

/// Output array contains each transformed optional value; ordering retained.
public func mapFilter<A, B>(xs : [A], f : A -> ?B) : [B] {
let ys : Buffer.Buffer<B> = Buffer.Buffer(xs.size());
for (x in xs.vals()) {
switch (f(x)) {
case null {};
case (?y) { ys.add(y) };
var count = 0;
let options =
Prim.Array_tabulate<?B>(
xs.size(),
func i {
let result = f(xs[i]);
switch (result) {
case (?element) {
count += 1;
result
};
case null {
null
}
}
}
);

var nextSome = 0;
Prim.Array_tabulate<B>(
count,
func _ {
while (Option.isNull(options[nextSome])) {
nextSome += 1;
};
nextSome += 1;
switch(options[nextSome - 1]) {
case(?element) element;
case null {
Prim.trap "Malformed array in mapFilter"
}
}
}
};
ys.toArray();
)
};

/// Aggregate and transform values into a single output value, by increasing indices.
public func foldLeft<A, B>(xs : [A], initial : B, f : (B, A) -> B) : B {
var acc = initial;
Expand Down Expand Up @@ -296,5 +340,5 @@ module {
xs[size - 1 - n];
});
};

}

Loading

0 comments on commit d52aecd

Please sign in to comment.