Skip to content

Commit d446eca

Browse files
authored
add rcmap and rcfilter (#350)
* add rcmap and rcfilter * more tests
1 parent 83e0975 commit d446eca

3 files changed

Lines changed: 265 additions & 5 deletions

File tree

source/mir/algorithm/iteration.d

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4089,6 +4089,105 @@ version(mir_test)
40894089
assert(equal!equal(rc, [ [3, 100], [], [400, 102] ]));
40904090
}
40914091

4092+
/++
4093+
Implements the higher order filter and map function. The predicate and map functions are passed to
4094+
`mir.functional.naryFun`, and can either accept a string, or any callable
4095+
that can be executed via `pred(element)` and `map(element)`.
4096+
Params:
4097+
pred = Filter function to apply to each element of range (optional)
4098+
map = Map function to apply to each element of range
4099+
Returns:
4100+
`rcfilter!(pred)(range)` returns a new RCArray containing only elements `map(x)` in `range` for
4101+
which `pred(x)` returns `true`.
4102+
See_Also:
4103+
$(HTTP en.wikipedia.org/wiki/Filter_(higher-order_function), Filter (higher-order function))
4104+
+/
4105+
template rcfilter(alias pred = "a", alias map = "a")
4106+
{
4107+
static if (__traits(isSame, naryFun!pred, pred) && __traits(isSame, naryFun!map, map))
4108+
{
4109+
/++
4110+
Params:
4111+
r = An input range of elements to filter.
4112+
Returns:
4113+
A new range containing only elements `x` in `range` for which `predicate(x)` returns `true`.
4114+
+/
4115+
auto rcfilter(Range)(Range r)
4116+
if (isIterable!Range && (!isSlice!Range || DimensionCount!Range == 1))
4117+
{
4118+
import core.lifetime: forward;
4119+
import std.range.primitives: isInputRange;
4120+
import mir.rc.array: RCArray;
4121+
4122+
if (false)
4123+
{
4124+
auto p = pred(r.front);
4125+
auto e = map(r.front);
4126+
r.popFront;
4127+
auto d = r.empty;
4128+
}
4129+
return () @trusted
4130+
{
4131+
import mir.appender: ScopedBuffer;
4132+
alias T = typeof(map(r.front));
4133+
ScopedBuffer!T buffer = void;
4134+
buffer.initialize;
4135+
foreach (ref e; r)
4136+
{
4137+
if (pred(e))
4138+
{
4139+
static if (__traits(isSame, naryFun!"a", map))
4140+
buffer.put(forward!e);
4141+
else
4142+
buffer.put(map(forward!e));
4143+
}
4144+
}
4145+
auto ret = RCArray!T(buffer.length);
4146+
buffer.moveDataAndEmplaceTo(ret[]);
4147+
return ret;
4148+
} ();
4149+
}
4150+
4151+
/// ditto
4152+
auto rcfilter(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
4153+
if (N > 1)
4154+
{
4155+
import mir.ndslice.topology: flattened;
4156+
import core.lifetime: move;
4157+
return rcfilter(slice.move.flattened);
4158+
}
4159+
}
4160+
else
4161+
alias rcfilter = .rcfilter!(naryFun!pred, naryFun!map);
4162+
}
4163+
4164+
///
4165+
version(mir_test)
4166+
@safe pure nothrow @nogc unittest
4167+
{
4168+
import mir.ndslice.topology: iota;
4169+
4170+
auto val = 3;
4171+
auto factor = 5;
4172+
// Filter iota 2x3 matrix below 3
4173+
assert(iota(2, 3).rcfilter!(a => a < val).moveToSlice.equal(val.iota));
4174+
// Filter and map below 3
4175+
assert(6.iota.rcfilter!(a => a < val, a => a * factor).moveToSlice.equal(val.iota * factor));
4176+
}
4177+
4178+
version(mir_test)
4179+
@safe pure nothrow @nogc unittest
4180+
{
4181+
import mir.ndslice.topology: iota, as;
4182+
4183+
auto val = 3;
4184+
auto factor = 5;
4185+
// Filter iota 2x3 matrix below 3
4186+
assert(iota(2, 3).as!(const int).rcfilter!(a => a < val).moveToSlice.equal(val.iota));
4187+
// Filter and map below 3
4188+
assert(6.iota.as!(immutable int).rcfilter!(a => a < val, a => a * factor).moveToSlice.equal(val.iota * factor));
4189+
}
4190+
40924191
/++
40934192
Implements the homonym function (also known as `accumulate`, $(D
40944193
compress), `inject`, or `foldl`) present in various programming

source/mir/ndslice/topology.d

Lines changed: 166 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2731,7 +2731,7 @@ Note:
27312731
Params:
27322732
fun = One or more functions.
27332733
See_Also:
2734-
$(LREF cached), $(LREF vmap), $(LREF indexed),
2734+
$(LREF cached), $(LREF vmap), $(LREF rcmap), $(LREF indexed),
27352735
$(LREF pairwise), $(LREF subSlices), $(LREF slide), $(LREF zip),
27362736
$(HTTP en.wikipedia.org/wiki/Map_(higher-order_function), Map (higher-order function))
27372737
+/
@@ -3041,7 +3041,7 @@ Params:
30413041
slice = ndslice
30423042
callable = callable object, structure, delegate, or function pointer.
30433043
See_Also:
3044-
$(LREF cached), $(LREF map), $(LREF indexed),
3044+
$(LREF cached), $(LREF map), $(LREF rcmap), $(LREF indexed),
30453045
$(LREF pairwise), $(LREF subSlices), $(LREF slide), $(LREF zip),
30463046
$(HTTP en.wikipedia.org/wiki/Map_(higher-order_function), Map (higher-order function))
30473047
+/
@@ -3181,7 +3181,7 @@ version(none) version(mir_test) unittest
31813181
}
31823182
}
31833183

3184-
/// Use map with byDim/alongDim to apply functions to each dimension
3184+
/// Use vmap with byDim/alongDim to apply functions to each dimension
31853185
version(mir_test)
31863186
@safe pure
31873187
unittest
@@ -3214,7 +3214,7 @@ unittest
32143214
}
32153215

32163216
/++
3217-
Use map with a lambda and with byDim/alongDim, but may need to allocate result.
3217+
Use vmap with a lambda and with byDim/alongDim, but may need to allocate result.
32183218
This example uses fuse, which allocates. Note: fuse!1 will transpose the result.
32193219
+/
32203220
version(mir_test)
@@ -3291,6 +3291,168 @@ private auto unhideStride
32913291
return slice;
32923292
}
32933293

3294+
/++
3295+
Implements the homonym function (also known as `transform`) present
3296+
in many languages of functional flavor. The call `rmap!(fun)(slice)`
3297+
returns an RC array (1D) or RC slice (ND) of which elements are obtained by applying `fun`
3298+
for all elements in `slice`. The original slices are
3299+
not changed. Evaluation is done eagerly.
3300+
3301+
Note:
3302+
$(SUBREF dynamic, transposed) and
3303+
$(SUBREF topology, pack) can be used to specify dimensions.
3304+
Params:
3305+
fun = One or more functions.
3306+
See_Also:
3307+
$(LREF cached), $(LREF map), $(LREF vmap), $(LREF indexed),
3308+
$(LREF pairwise), $(LREF subSlices), $(LREF slide), $(LREF zip),
3309+
$(HTTP en.wikipedia.org/wiki/Map_(higher-order_function), Map (higher-order function))
3310+
+/
3311+
template rcmap(fun...)
3312+
if (fun.length)
3313+
{
3314+
import mir.functional: adjoin, naryFun, pipe;
3315+
static if (fun.length == 1)
3316+
{
3317+
static if (__traits(isSame, naryFun!(fun[0]), fun[0]))
3318+
{
3319+
alias f = fun[0];
3320+
@optmath:
3321+
/++
3322+
Params:
3323+
slice = An ndslice, array, or an input range.
3324+
Returns:
3325+
ndslice or an input range with each fun applied to all the elements. If there is more than one
3326+
fun, the element type will be `Tuple` containing one element for each fun.
3327+
+/
3328+
auto rcmap(Iterator, size_t N, SliceKind kind)
3329+
(Slice!(Iterator, N, kind) slice)
3330+
{
3331+
import core.lifetime: move;
3332+
auto shape = slice.shape;
3333+
auto r = slice.move.flattened;
3334+
if (false)
3335+
{
3336+
auto e = f(r.front);
3337+
r.popFront;
3338+
auto d = r.empty;
3339+
}
3340+
return () @trusted
3341+
{
3342+
import mir.rc.array: RCArray;
3343+
import std.traits: Unqual;
3344+
import mir.conv: emplaceRef;
3345+
3346+
alias T = typeof(f(r.front));
3347+
auto ret = RCArray!T(r.length);
3348+
auto next = ret.ptr;
3349+
while (!r.empty)
3350+
{
3351+
emplaceRef(*cast(Unqual!T*)next++, f(r.front));
3352+
r.popFront;
3353+
}
3354+
static if (N == 1)
3355+
{
3356+
return ret;
3357+
}
3358+
else
3359+
{
3360+
return ret.moveToSlice.sliced(shape);
3361+
}
3362+
} ();
3363+
}
3364+
3365+
/// ditto
3366+
auto rcmap(T)(T[] array)
3367+
{
3368+
return rcmap(array.sliced);
3369+
}
3370+
3371+
/// ditto
3372+
auto rcmap(T)(T withAsSlice)
3373+
if (hasAsSlice!T)
3374+
{
3375+
static if (__traits(hasMember, T, "moveToSlice"))
3376+
return rcmap(withAsSlice.moveToSlice);
3377+
else
3378+
return rcmap(withAsSlice.asSlice);
3379+
}
3380+
3381+
/// ditto
3382+
auto rcmap(Range)(Range r)
3383+
if (!hasAsSlice!Range && !isSlice!Range && !is(Range : T[], T))
3384+
{
3385+
import core.lifetime: forward;
3386+
import std.range.primitives: isInputRange;
3387+
import mir.rc.array: RCArray;
3388+
3389+
if (false)
3390+
{
3391+
auto e = f(r.front);
3392+
r.popFront;
3393+
auto d = r.empty;
3394+
}
3395+
return () @trusted
3396+
{
3397+
import mir.appender: ScopedBuffer;
3398+
alias T = typeof(f(r.front));
3399+
ScopedBuffer!T buffer = void;
3400+
buffer.initialize;
3401+
while (!r.empty)
3402+
{
3403+
buffer.put(f(r.front));
3404+
r.popFront;
3405+
}
3406+
auto ret = RCArray!T(buffer.length, false);
3407+
buffer.moveDataAndEmplaceTo(ret[]);
3408+
return ret;
3409+
} ();
3410+
}
3411+
}
3412+
else alias rcmap = .rcmap!(staticMap!(naryFun, fun));
3413+
}
3414+
else alias rcmap = .rcmap!(adjoin!fun);
3415+
}
3416+
3417+
/// Returns RCArray for input ranges and one-dimensional slices.
3418+
@safe pure nothrow @nogc
3419+
version(mir_test) unittest
3420+
{
3421+
import mir.algorithm.iteration: filter, equal;
3422+
auto factor = 10;
3423+
auto step = 20;
3424+
assert (3.iota.rcmap!(a => a * factor).moveToSlice.equal(3.iota * factor));
3425+
assert (6.iota.filter!"a % 2".rcmap!(a => a * factor).moveToSlice.equal([3].iota(factor, step)));
3426+
}
3427+
3428+
/// For multidimensional case returns `Slice!(RCI!T, N)`.
3429+
@safe pure nothrow @nogc
3430+
version(mir_test) unittest
3431+
{
3432+
import mir.ndslice.topology : iota;
3433+
auto factor = 3;
3434+
auto s = iota(2, 3).rcmap!(a => a * factor);
3435+
assert(s == iota(2, 3) * factor);
3436+
}
3437+
3438+
/// String lambdas
3439+
@safe pure nothrow
3440+
version(mir_test) unittest
3441+
{
3442+
import mir.ndslice.topology : iota;
3443+
assert(iota(2, 3).rcmap!"a * 2" == iota(2, 3) * 2);
3444+
}
3445+
3446+
@safe pure nothrow @nogc
3447+
version(mir_test) unittest
3448+
{
3449+
import mir.algorithm.iteration: filter, equal;
3450+
auto factor = 10;
3451+
auto step = 20;
3452+
assert (3.iota.as!(const int).rcmap!(a => a * factor).moveToSlice.equal(3.iota * factor));
3453+
assert (6.iota.filter!"a % 2".as!(immutable int).rcmap!(a => a * factor).moveToSlice.equal([3].iota(factor, step)));
3454+
}
3455+
32943456
/++
32953457
Creates a random access cache for lazyly computed elements.
32963458
Params:

source/mir/rc/array.d

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -887,4 +887,3 @@ unittest
887887
RCArray!int value = null;
888888
value = null;
889889
}
890-

0 commit comments

Comments
 (0)