@@ -3563,28 +3563,48 @@ bidirectional, $(D uniq) also yields a
35633563`std,range,primitives`.
35643564Params:
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/+ +
35973632Authros: $(HTTP erdani.com, Andrei Alexandrescu) (original Phobos code), Ilya Yaroshenko (betterC rework)
35983633+/
35993634struct 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