MapRange

Implements the homonym function (also known as transform) present in many languages of functional flavor. The call map!(fun)(slice) returns a slice of which elements are obtained by applying fun for all elements in slice. The original slices are not changed. Evaluation is done lazily.

Note: transposed and pack can be used to specify dimensions.

Members

Functions

back
auto ref back()
Undocumented in source. Be warned that the author may not have intended to support it.
empty
bool empty()
Undocumented in source. Be warned that the author may not have intended to support it.
front
auto ref front()
Undocumented in source. Be warned that the author may not have intended to support it.
length
auto length()
Undocumented in source. Be warned that the author may not have intended to support it.
popBack
void popBack()
Undocumented in source. Be warned that the author may not have intended to support it.
popFront
void popFront()
Undocumented in source. Be warned that the author may not have intended to support it.
save
auto save()
Undocumented in source. Be warned that the author may not have intended to support it.

Variables

_input
Range _input;
Undocumented in source.
empty
enum bool empty;
Undocumented in source.

Parameters

fun

One or more functions.

Examples

import mir.ndslice.topology : iota;
auto s = iota(2, 3).map!(a => a * 3);
assert(s == [[ 0,  3,  6],
             [ 9, 12, 15]]);

String lambdas

import mir.ndslice.topology : iota;
assert(iota(2, 3).map!"a * 2" == [[0, 2, 4], [6, 8, 10]]);

Input ranges

import mir.algorithm.iteration: filter, equal;
assert (6.iota.filter!"a % 2".map!"a * 10".equal([10, 30, 50])); 

Packed tensors

import mir.ndslice.topology : iota, windows;
import mir.math.sum: sum;

//  iota        windows     map  sums ( reduce!"a + b" )
//                --------------
//  -------      |  ---    ---  |      ------
// | 0 1 2 |  => || 0 1 || 1 2 ||  => | 8 12 |
// | 3 4 5 |     || 3 4 || 4 5 ||      ------
//  -------      |  ---    ---  |
//                --------------
auto s = iota(2, 3)
    .windows(2, 2)
    .map!sum;

assert(s == [[8, 12]]);

Zipped tensors

import mir.ndslice.topology : iota, zip;

// 0 1 2
// 3 4 5
auto sl1 = iota(2, 3);
// 1 2 3
// 4 5 6
auto sl2 = iota([2, 3], 1);

auto z = zip(sl1, sl2);

assert(zip(sl1, sl2).map!"a + b" == sl1 + sl2);
assert(zip(sl1, sl2).map!((a, b) => a + b) == sl1 + sl2);

Multiple functions can be passed to map. In that case, the element type of map is a refTuple containing one element for each function.

import mir.ndslice.topology : iota;

auto sl = iota(2, 3);
auto s = sl.map!("a + a", "a * a");

auto sums     = [[0, 2, 4], [6,  8, 10]];
auto products = [[0, 1, 4], [9, 16, 25]];

assert(s.map!"a[0]" == sl + sl);
assert(s.map!"a[1]" == sl * sl);

map can be aliased to a symbol and be used separately:

import mir.ndslice.topology : iota;

alias halfs = map!"double(a) / 2";
assert(halfs(iota(2, 3)) == [[0.0, 0.5, 1], [1.5, 2, 2.5]]);

Type normalization

import mir.functional : pipe;
import mir.ndslice.topology : iota;
auto a = iota(2, 3).map!"a + 10".map!(pipe!("a * 2", "a + 1"));
auto b = iota(2, 3).map!(pipe!("a + 10", "a * 2", "a + 1"));
assert(a == b);
static assert(is(typeof(a) == typeof(b)));

Use map with byDim/alongDim to apply functions to each dimension

import mir.ndslice.topology: byDim, alongDim;
import mir.ndslice.fuse: fuse;
import mir.math.stat: mean;
import mir.algorithm.iteration: all;
import mir.math.common: approxEqual;

auto x = [
    [0.0, 1.0, 1.5, 2.0, 3.5, 4.25],
    [2.0, 7.5, 5.0, 1.0, 1.5, 0.0]
].fuse;

// Use byDim/alongDim with map to compute mean of row/column.
assert(x.byDim!0.map!mean.all!approxEqual([12.25 / 6, 17.0 / 6]));
assert(x.byDim!1.map!mean.all!approxEqual([1, 4.25, 3.25, 1.5, 2.5, 2.125]));
assert(x.alongDim!1.map!mean.all!approxEqual([12.25 / 6, 17.0 / 6]));
assert(x.alongDim!0.map!mean.all!approxEqual([1, 4.25, 3.25, 1.5, 2.5, 2.125]));

Use map with a lambda and with byDim/alongDim, but may need to allocate result. This example uses fuse, which allocates. Note: fuse!1 will transpose the result.

import mir.ndslice.topology: iota, byDim, alongDim, map;
import mir.ndslice.fuse: fuse;
import mir.ndslice.slice: sliced;

auto x = [1, 2, 3].sliced;
auto y = [1, 2].sliced;

auto s1 = iota(2, 3).byDim!0.map!(a => a * x).fuse;
assert(s1 == [[ 0, 2,  6],
              [ 3, 8, 15]]);
auto s2 = iota(2, 3).byDim!1.map!(a => a * y).fuse!1;
assert(s2 == [[ 0, 1,  2],
              [ 6, 8, 10]]);
auto s3 = iota(2, 3).alongDim!1.map!(a => a * x).fuse;
assert(s1 == [[ 0, 2,  6],
              [ 3, 8, 15]]);
auto s4 = iota(2, 3).alongDim!0.map!(a => a * y).fuse!1;
assert(s2 == [[ 0, 1,  2],
              [ 6, 8, 10]]);
import mir.algorithm.iteration: reduce;
import mir.math.common: fmax;
import mir.math.stat: mean;
import mir.math.sum;
/// Returns maximal column average.
auto maxAvg(S)(S matrix) {
    return reduce!fmax(0.0, matrix.alongDim!1.map!mean);
}
// 1 2
// 3 4
auto matrix = iota([2, 2], 1);
assert(maxAvg(matrix) == 3.5);

See Also

Meta