@@ -3563,28 +3563,48 @@ bidirectional, $(D uniq) also yields a
3563
3563
`std,range,primitives`.
3564
3564
Params:
3565
3565
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.
3571
3566
*/
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" )
3574
3568
{
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);
3576
3597
}
3577
3598
3578
3599
// /
3579
3600
@safe version(mir_test) unittest
3580
3601
{
3581
- import std.algorithm.mutation : copy;
3582
-
3583
3602
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 ]));
3585
3604
3605
+ import std.algorithm.mutation : copy;
3586
3606
// Filter duplicates in-place using copy
3587
- arr.length -= arr.uniq() .copy(arr).length;
3607
+ arr.length -= arr.uniq.copy(arr).length;
3588
3608
assert (arr == [ 1 , 2 , 3 , 4 , 5 ]);
3589
3609
3590
3610
// Note that uniqueness is only determined consecutively; duplicated
@@ -3593,20 +3613,28 @@ if (isInputRange!Range && is(typeof(naryFun!pred(r.front, r.front)) == bool))
3593
3613
assert (equal(uniq([ 1 , 1 , 2 , 1 , 1 , 3 , 1 ]), [1 , 2 , 1 , 3 , 1 ]));
3594
3614
}
3595
3615
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
+
3596
3631
/+ +
3597
3632
Authros: $(HTTP erdani.com, Andrei Alexandrescu) (original Phobos code), Ilya Yaroshenko (betterC rework)
3598
3633
+/
3599
3634
struct Uniq (alias pred, Range )
3600
3635
{
3601
3636
Range _input;
3602
3637
3603
- // this()(auto ref Range input)
3604
- // {
3605
- // alias AliasSeq(T...) = T;
3606
- // import core.lifetime: forward;
3607
- // AliasSeq!_input = forward!input;
3608
- // }
3609
-
3610
3638
ref opSlice () inout
3611
3639
{
3612
3640
return this ;
@@ -3623,7 +3651,7 @@ struct Uniq(alias pred, Range)
3623
3651
while (! _input.empty && pred(last, _input.front));
3624
3652
}
3625
3653
3626
- @property ElementType ! Range front()
3654
+ auto ref front () @property
3627
3655
{
3628
3656
assert (! empty, " Attempting to fetch the front of an empty uniq." );
3629
3657
return _input.front;
@@ -3642,7 +3670,7 @@ struct Uniq(alias pred, Range)
3642
3670
while (! _input.empty && pred(last, _input.back));
3643
3671
}
3644
3672
3645
- @property ElementType ! Range back() scope return
3673
+ auto ref back () scope return @property
3646
3674
{
3647
3675
assert (! empty, " Attempting to fetch the back of an empty uniq." );
3648
3676
return _input.back;
@@ -3660,7 +3688,8 @@ struct Uniq(alias pred, Range)
3660
3688
3661
3689
static if (isForwardRange! Range )
3662
3690
{
3663
- @property typeof (this ) save() scope return {
3691
+ @property typeof (this ) save() scope return
3692
+ {
3664
3693
return typeof (this )(_input.save);
3665
3694
}
3666
3695
}
@@ -3709,3 +3738,153 @@ version(none)
3709
3738
y[] = [2 , 3 ];
3710
3739
assert (equal! approxEqual(x,y));
3711
3740
}
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