questions around mutating range algorithms, const, and return ref

Ali Çehreli acehreli at yahoo.com
Mon Jan 29 06:46:26 UTC 2018


On 01/28/2018 02:52 PM, aliak wrote:
 > Hello, I'm trying to write a function called "pull" that, given 2
 > ranges, "pull"s the values from range 2 out of range 1. I'm not sure if
 > I'm doing it correctly though, and I have some questions so any help is
 > appreciated. This is what I have:
 >
 > ref pull(R1, R2)(return ref R1 r1, R2 r2) {
 >      import std.algorithm, std.array, std.range;
 >      int numFound = 0;
 >      auto r = r1.save;
 >      ElementType!R1[] unfoundElements;
 >      foreach (e; r) {
 >          if (!r2.canFind(e)) {
 >              unfoundElements ~= e;
 >          } else {
 >              numFound++;
 >          }
 >      }
 >      moveEmplaceAll(unfoundElements, r1);
 >      r1.popBackN(numFound);
 >      return r1;
 > }
 >
 > So my questions are:
 >
 > 1) So first of all, is there a function like this in phobos that I can
 > use? From what I understand phobos is supposed to be nogc so mutating
 > functions like these are usually in place ones?

I think the following trivial wrapper around std.algorithm.remove() 
should do:

void removeMatching(R, N)(ref R r, N needles) {
     import std.algorithm : remove, canFind;
     r = r.remove!(e => needles.canFind(e));
}

void main() {
     auto arr = [1, 2, 3, 2, 4];
     arr.removeMatching([2, 3]);
     assert(arr == [1, 4]);
}

 > 2) Is there a way to avoid the extra "moveEmplaceAll" call towards the
 > end and still get the same results?

I'm not convinced that unfoundElements is needed at all. :)

 > 3) Is it necessary to always import std.array (or std.range: empty,
 > front) when doing work like this with range algorithms? (otherwise you
 > get no property save when an array is used as a range)

I think importing std.range works as well.

 > 6) It will not work when I pass in a range that has const elements.
 >
 > ie:
 >
 > const(int)[] arr = [1, 2, 3, 4, 5];
 > arr.pull([2, 3]);
 >
 > This I guess makes sense because moveEmplaceAll depends on isInputRange
 > and from my understanding, a const range cannot be one (is that
 > correct?).

Yes but your range is not const, the elements are. So, although 
const(int)[] is a range, it does not have mutable elements.

 > So am I correct in thinking there really is no way around
 > this, or is there some move/cast trickery that I can use maybe?

You don't want to mutate const elements anyway. It would be breaking a 
promise that other parts of the program may be depending on. I would 
return a filtered-out range:

import std.algorithm;

void main() {
     const(int)[] arr = [1, 2, 3, 2, 4];
     auto result = arr.filter!(e => ![2, 3].canFind(e));
     assert(result.equal([1, 4]));
}

If needed, the caller can easily make an array out of the filtered range 
and the element types will still be const(int):

     import std.range;
     auto arr2 = result.array();
     static assert(is(typeof(arr2) == const(int)[]));

 >
 > Cheers,
 > - Ali

Ali



More information about the Digitalmars-d-learn mailing list