Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add map, fold, reduce, any, and all for slices #5331

Merged
merged 2 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/docs/noir/concepts/data_types/arrays.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ fn main() {

### reduce

Same as fold, but uses the first element as starting element.
Same as fold, but uses the first element as the starting element.

```rust
fn reduce(self, f: fn(T, T) -> T) -> T
Expand Down
105 changes: 105 additions & 0 deletions docs/docs/noir/concepts/data_types/slices.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -191,3 +191,108 @@ fn main() {
assert(array[1] == slice[1]);
}
```

### map

Applies a function to each element of the slice, returning a new slice containing the mapped elements.

```rust
fn map<U>(self, f: fn(T) -> U) -> [U]
```

example

```rust
let a = &[1, 2, 3];
let b = a.map(|a| a * 2); // b is now &[2, 4, 6]
```

### fold

Applies a function to each element of the slice, returning the final accumulated value. The first
parameter is the initial value.

```rust
fn fold<U>(self, mut accumulator: U, f: fn(U, T) -> U) -> U
```

This is a left fold, so the given function will be applied to the accumulator and first element of
the slice, then the second, and so on. For a given call the expected result would be equivalent to:

```rust
let a1 = &[1];
let a2 = &[1, 2];
let a3 = &[1, 2, 3];

let f = |a, b| a - b;
a1.fold(10, f) //=> f(10, 1)
a2.fold(10, f) //=> f(f(10, 1), 2)
a3.fold(10, f) //=> f(f(f(10, 1), 2), 3)
```

example:

```rust

fn main() {
let slice = &[2, 2, 2, 2, 2];
let folded = slice.fold(0, |a, b| a + b);
assert(folded == 10);
}

```

### reduce

Same as fold, but uses the first element as the starting element.

```rust
fn reduce(self, f: fn(T, T) -> T) -> T
```

example:

```rust
fn main() {
let slice = &[2, 2, 2, 2, 2];
let reduced = slice.reduce(|a, b| a + b);
assert(reduced == 10);
}
```

### all

Returns true if all the elements satisfy the given predicate

```rust
fn all(self, predicate: fn(T) -> bool) -> bool
```

example:

```rust
fn main() {
let slice = &[2, 2, 2, 2, 2];
let all = slice.all(|a| a == 2);
assert(all);
}
```

### any

Returns true if any of the elements satisfy the given predicate

```rust
fn any(self, predicate: fn(T) -> bool) -> bool
```

example:

```rust
fn main() {
let slice = &[2, 2, 2, 2, 5];
let any = slice.any(|a| a == 5);
assert(any);
}

```
49 changes: 49 additions & 0 deletions noir_stdlib/src/slice.nr
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,53 @@ impl<T> [T] {
}
array
}

// Apply a function to each element of the slice, returning a new slice
// containing the mapped elements.
pub fn map<U, Env>(self, f: fn[Env](T) -> U) -> [U] {
let mut ret = &[];
for elem in self {
ret = ret.push_back(f(elem));
}
ret
}

// Apply a function to each element of the slice and an accumulator value,
// returning the final accumulated value. This function is also sometimes
// called `foldl`, `fold_left`, `reduce`, or `inject`.
pub fn fold<U, Env>(self, mut accumulator: U, f: fn[Env](U, T) -> U) -> U {
for elem in self {
accumulator = f(accumulator, elem);
}
accumulator
}

// Apply a function to each element of the slice and an accumulator value,
// returning the final accumulated value. Unlike fold, reduce uses the first
// element of the given slice as its starting accumulator value.
pub fn reduce<Env>(self, f: fn[Env](T, T) -> T) -> T {
let mut accumulator = self[0];
for i in 1..self.len() {
accumulator = f(accumulator, self[i]);
}
accumulator
}

// Returns true if all elements in the slice satisfy the predicate
pub fn all<Env>(self, predicate: fn[Env](T) -> bool) -> bool {
let mut ret = true;
for elem in self {
ret &= predicate(elem);
}
ret
}

// Returns true if any element in the slice satisfies the predicate
pub fn any<Env>(self, predicate: fn[Env](T) -> bool) -> bool {
let mut ret = false;
for elem in self {
ret |= predicate(elem);
}
ret
}
}
8 changes: 8 additions & 0 deletions test_programs/execution_success/slices/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ fn main(x: Field, y: pub Field) {
assert(append[0] == 1);
assert(append[4] == 5);

let mapped = &[1, 2].map(|x| x + 1);
assert_eq(mapped, &[2, 3]);

assert_eq(&[1, 2, 3].fold(0, |acc, x| acc + x), 6);
assert_eq(&[1, 2, 3].reduce(|acc, x| acc + x), 6);
assert(&[2, 4, 6].all(|x| x > 0));
assert(&[2, 4, 6].any(|x| x > 5));

regression_2083();
// The parameters to this function must come from witness values (inputs to main)
regression_merge_slices(x, y);
Expand Down
Loading