Skip to content

Commit 7760a59

Browse files
authored
add filter, update uniq, fix concatenation (#247)
* add filter, update uniq, fix concatenation * fixup * add nd case
1 parent 0f50d8a commit 7760a59

File tree

7 files changed

+307
-60
lines changed

7 files changed

+307
-60
lines changed

source/mir/algorithm/iteration.d

+201-22
Original file line numberDiff line numberDiff line change
@@ -3563,28 +3563,48 @@ bidirectional, $(D uniq) also yields a
35633563
`std,range,primitives`.
35643564
Params:
35653565
pred = Predicate for determining equivalence between range elements.
3566-
r = An input range of elements to filter.
3567-
Returns:
3568-
An input range of
3569-
consecutively unique elements in the original range. If `r` is also a
3570-
forward range or bidirectional range, the returned range will be likewise.
35713566
*/
3572-
Uniq!(naryFun!pred, Range) uniq(alias pred = "a == b", Range)(auto ref Range r)
3573-
if (isInputRange!Range && is(typeof(naryFun!pred(r.front, r.front)) == bool))
3567+
template uniq(alias pred = "a == b")
35743568
{
3575-
return typeof(return)(r);
3569+
static if (__traits(isSame, naryFun!pred, pred))
3570+
{
3571+
/++
3572+
Params:
3573+
r = An input range of elements to filter.
3574+
Returns:
3575+
An input range of
3576+
consecutively unique elements in the original range. If `r` is also a
3577+
forward range or bidirectional range, the returned range will be likewise.
3578+
+/
3579+
Uniq!(naryFun!pred, Range) uniq(Range)(Range r)
3580+
if (isInputRange!Range && !isSlice!Range)
3581+
{
3582+
import core.lifetime: move;
3583+
return typeof(return)(r.move);
3584+
}
3585+
3586+
/// ditto
3587+
auto uniq(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
3588+
{
3589+
import mir.ndslice.topology: flattened;
3590+
import core.lifetime: move;
3591+
auto r = slice.move.flattened;
3592+
return Uniq!(pred, typeof(r))(move(r));
3593+
}
3594+
}
3595+
else
3596+
alias uniq = .uniq!(naryFun!pred);
35763597
}
35773598

35783599
///
35793600
@safe version(mir_test) unittest
35803601
{
3581-
import std.algorithm.mutation : copy;
3582-
35833602
int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ];
3584-
assert(equal(uniq(arr), [ 1, 2, 3, 4, 5 ][]));
3603+
assert(equal(uniq(arr), [ 1, 2, 3, 4, 5 ]));
35853604

3605+
import std.algorithm.mutation : copy;
35863606
// Filter duplicates in-place using copy
3587-
arr.length -= arr.uniq().copy(arr).length;
3607+
arr.length -= arr.uniq.copy(arr).length;
35883608
assert(arr == [ 1, 2, 3, 4, 5 ]);
35893609

35903610
// Note that uniqueness is only determined consecutively; duplicated
@@ -3593,20 +3613,28 @@ if (isInputRange!Range && is(typeof(naryFun!pred(r.front, r.front)) == bool))
35933613
assert(equal(uniq([ 1, 1, 2, 1, 1, 3, 1]), [1, 2, 1, 3, 1]));
35943614
}
35953615

3616+
/// N-dimensional case
3617+
version(mir_test)
3618+
@safe pure unittest
3619+
{
3620+
import mir.ndslice.fuse;
3621+
import mir.ndslice.topology: byDim, map, iota;
3622+
3623+
auto matrix = [ [1, 2, 2], [2, 2, 3], [4, 4, 4] ].fuse;
3624+
3625+
assert(matrix.uniq.equal([ 1, 2, 3, 4 ]));
3626+
3627+
// unique elements for each row
3628+
assert(matrix.byDim!0.map!uniq.equal!equal([ [1, 2], [2, 3], [4] ]));
3629+
}
3630+
35963631
/++
35973632
Authros: $(HTTP erdani.com, Andrei Alexandrescu) (original Phobos code), Ilya Yaroshenko (betterC rework)
35983633
+/
35993634
struct Uniq(alias pred, Range)
36003635
{
36013636
Range _input;
36023637

3603-
// this()(auto ref Range input)
3604-
// {
3605-
// alias AliasSeq(T...) = T;
3606-
// import core.lifetime: forward;
3607-
// AliasSeq!_input = forward!input;
3608-
// }
3609-
36103638
ref opSlice() inout
36113639
{
36123640
return this;
@@ -3623,7 +3651,7 @@ struct Uniq(alias pred, Range)
36233651
while (!_input.empty && pred(last, _input.front));
36243652
}
36253653

3626-
@property ElementType!Range front()
3654+
auto ref front() @property
36273655
{
36283656
assert(!empty, "Attempting to fetch the front of an empty uniq.");
36293657
return _input.front;
@@ -3642,7 +3670,7 @@ struct Uniq(alias pred, Range)
36423670
while (!_input.empty && pred(last, _input.back));
36433671
}
36443672

3645-
@property ElementType!Range back() scope return
3673+
auto ref back() scope return @property
36463674
{
36473675
assert(!empty, "Attempting to fetch the back of an empty uniq.");
36483676
return _input.back;
@@ -3660,7 +3688,8 @@ struct Uniq(alias pred, Range)
36603688

36613689
static if (isForwardRange!Range)
36623690
{
3663-
@property typeof(this) save() scope return {
3691+
@property typeof(this) save() scope return
3692+
{
36643693
return typeof(this)(_input.save);
36653694
}
36663695
}
@@ -3709,3 +3738,153 @@ version(none)
37093738
y[] = [2, 3];
37103739
assert(equal!approxEqual(x,y));
37113740
}
3741+
3742+
/++
3743+
Implements the higher order filter function. The predicate is passed to
3744+
`mir.functional.naryFun`, and can either accept a string, or any callable
3745+
that can be executed via `pred(element)`.
3746+
Params:
3747+
pred = Function to apply to each element of range
3748+
Returns:
3749+
`filter!(pred)(range)` returns a new range containing only elements `x` in `range` for
3750+
which `pred(x)` returns `true`.
3751+
See_Also:
3752+
$(HTTP en.wikipedia.org/wiki/Filter_(higher-order_function), Filter (higher-order function))
3753+
Note:
3754+
$(RED User and library code MUST call `empty` method ahead each call of pair or one of `front` and `popFront` methods.)
3755+
+/
3756+
template filter(alias pred = "a")
3757+
{
3758+
static if (__traits(isSame, naryFun!pred, pred))
3759+
{
3760+
/++
3761+
Params:
3762+
r = An input range of elements to filter.
3763+
Returns:
3764+
A new range containing only elements `x` in `range` for which `predicate(x)` returns `true`.
3765+
+/
3766+
Filter!(naryFun!pred, Range) filter(Range)(Range r)
3767+
if (isInputRange!Range && !isSlice!Range)
3768+
{
3769+
import core.lifetime: move;
3770+
return typeof(return)(r.move);
3771+
}
3772+
3773+
/// ditto
3774+
auto filter(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
3775+
{
3776+
import mir.ndslice.topology: flattened;
3777+
import core.lifetime: move;
3778+
auto r = slice.move.flattened;
3779+
return Filter!(pred, typeof(r))(move(r));
3780+
}
3781+
}
3782+
else
3783+
alias filter = .filter!(naryFun!pred);
3784+
}
3785+
3786+
/// ditto
3787+
struct Filter(alias pred, Range)
3788+
{
3789+
Range _input;
3790+
version(assert) bool _freshEmpty;
3791+
3792+
ref opSlice() inout
3793+
{
3794+
return this;
3795+
}
3796+
3797+
void popFront() scope
3798+
{
3799+
assert(!_input.empty, "Attempting to popFront an empty Filter.");
3800+
assert(_freshEmpty, "Attempting to pop the front of a Filter without calling '.empty' method ahead.");
3801+
version(assert) _freshEmpty = false;
3802+
_input.popFront;
3803+
}
3804+
3805+
auto ref front() @property
3806+
{
3807+
assert(!_input.empty, "Attempting to fetch the front of an empty Filter.");
3808+
assert(_freshEmpty, "Attempting to fetch the front of a Filter without calling '.empty' method ahead.");
3809+
return _input.front;
3810+
}
3811+
3812+
bool empty() @property
3813+
{
3814+
version(assert) _freshEmpty = true;
3815+
for (;;)
3816+
{
3817+
if (auto r = _input.empty)
3818+
return true;
3819+
if (pred(_input.front))
3820+
return false;
3821+
_input.popFront;
3822+
}
3823+
}
3824+
3825+
static if (isForwardRange!Range)
3826+
{
3827+
@property typeof(this) save() scope return
3828+
{
3829+
return typeof(this)(_input.save);
3830+
}
3831+
}
3832+
}
3833+
3834+
///
3835+
version(mir_test)
3836+
@safe pure nothrow unittest
3837+
{
3838+
int[] arr = [ 0, 1, 2, 3, 4, 5 ];
3839+
3840+
// Filter below 3
3841+
auto small = filter!(a => a < 3)(arr);
3842+
assert(equal(small, [ 0, 1, 2 ]));
3843+
3844+
// Filter again, but with Uniform Function Call Syntax (UFCS)
3845+
auto sum = arr.filter!(a => a < 3);
3846+
assert(equal(sum, [ 0, 1, 2 ]));
3847+
3848+
// Filter with the default predicate
3849+
auto nonZeros = arr.filter;
3850+
assert(equal(nonZeros, [ 1, 2, 3, 4, 5 ]));
3851+
3852+
// In combination with concatenation() to span multiple ranges
3853+
import mir.ndslice.concatenation;
3854+
3855+
int[] a = [ 3, -2, 400 ];
3856+
int[] b = [ 100, -101, 102 ];
3857+
auto r = concatenation(a, b).filter!(a => a > 0);
3858+
assert(equal(r, [ 3, 400, 100, 102 ]));
3859+
3860+
// Mixing convertible types is fair game, too
3861+
double[] c = [ 2.5, 3.0 ];
3862+
auto r1 = concatenation(c, a, b).filter!(a => cast(int) a != a);
3863+
assert(equal(r1, [ 2.5 ]));
3864+
}
3865+
3866+
/// N-dimensional filtering
3867+
version(mir_test)
3868+
@safe pure unittest
3869+
{
3870+
import mir.ndslice.fuse;
3871+
import mir.ndslice.topology: byDim, map;
3872+
3873+
auto matrix =
3874+
[[ 3, -2, 400 ],
3875+
[ 100, -101, 102 ]].fuse;
3876+
3877+
alias filterPositive = filter!"a > 0";
3878+
3879+
// filter all elements in the matrix
3880+
auto r = filterPositive(matrix);
3881+
assert(equal(r, [ 3, 400, 100, 102 ]));
3882+
3883+
// filter all elements for each row
3884+
auto rr = matrix.byDim!0.map!filterPositive;
3885+
assert(equal!equal(rr, [ [3, 400], [100, 102] ]));
3886+
3887+
// filter all elements for each column
3888+
auto rc = matrix.byDim!1.map!filterPositive;
3889+
assert(equal!equal(rc, [ [3, 100], [], [400, 102] ]));
3890+
}

0 commit comments

Comments
 (0)