digitalmars.D.learn - questions around mutating range algorithms, const, and return ref
- aliak (48/48) Jan 28 2018 Hello, I'm trying to write a function called "pull" that, given 2
- =?UTF-8?Q?Ali_=c3=87ehreli?= (31/71) Jan 28 2018 I think the following trivial wrapper around std.algorithm.remove()
- aliak (25/50) Jan 29 2018 Haha awesome! Yes that's much better :)
- Simen =?UTF-8?B?S2rDpnLDpXM=?= (18/27) Jan 29 2018 Consider this case:
- aliak (39/55) Jan 30 2018 Ooh my... yes I can understand that. And I guess immutable is
- =?UTF-8?Q?Ali_=c3=87ehreli?= (11/31) Jan 30 2018 It's the same with C++: A type with a const member cannot have a
- aliak (3/26) Jan 30 2018 ah, ok. Gotcha, thanks again, Ali.
- Seb (2/15) Jan 29 2018 Not too hard to fix: https://github.com/dlang/phobos/pull/6090
- aliak (2/19) Jan 30 2018 Nice! :D
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? 2) Is there a way to avoid the extra "moveEmplaceAll" call towards the end and still get the same results? 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) 4) is the .save necessary? My (initial) tests seem to show that it works without, but from my understanding of what save does, it feels like it's necessary. 5) return ref and -dip25 ... is that something that's going to become default at some time? Is there a timeline? (also curious about dip1000 for that matter). 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?). 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? Cheers, - Ali
Jan 28 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, - AliAli
Jan 28 2018
On Monday, 29 January 2018 at 06:46:26 UTC, Ali Çehreli wrote: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)); }Haha awesome! Yes that's much better :) Note to self: learn to scroll down in the docs to find other definitions. I was convinced that remove only acted on indices :pI'm not convinced that unfoundElements is needed at all. :)Can't argue with that :)Ah right, yes the range is not const indeed.6) It will not work when I pass in a range that has constelements.ie: const(int)[] arr = [1, 2, 3, 4, 5]; arr.pull([2, 3]); This I guess makes sense because moveEmplaceAll depends onisInputRangeand from my understanding, a const range cannot be one (isthatcorrect?).Yes but your range is not const, the elements are. So, although const(int)[] is a range, it does not have mutable elements.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:Right, I want to mutate the range though, not the elements. So moving things from one location is not considered as a const violation in c++ for eg, maybe the D way is different though? Any reading material there? Also hasMobileElements!(const int[]) is true, so that means I can move elements around right? If I can move elements, that means I should be able to move the ones I want to the beginning, of the range, but then I'm also unsure about how to go about changing the size of a range. popBack on a mutable range with const data might cause destructors to be called, so I'm going to say that should be ok because as a user, if you're calling a range that mutates by removing things, and if you try and access those things later that's probably along the same lines as removing elements from a range that you're "foreach"ing over. And your second approach is definitely more practical I'd say, but I'm going to go about this as a learning exercise in dealing with mutations, and const with ranges in D. Thanks you for the comments!
Jan 29 2018
On Monday, 29 January 2018 at 11:36:26 UTC, aliak wrote:Consider this case: immutable(int)[] a = [1,2,3]; immutable(int)* b = &a[1]; You're free to slice the array or pop elements off its front or back, but if you change the order of elements, the value that b points to will change. D does not allow this. You can create a new array with the elements moved into the locations you want, and with another layer of indirections (int*[]) you can move the elements of the array without mutating the values.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:Right, I want to mutate the range though, not the elements. So moving things from one location is not considered as a const violation in c++ for eg, maybe the D way is different though?Any reading material there?https://dlang.org/const-faq.htmlAlso hasMobileElements!(const int[]) is true, so that means I can move elements around right?hasMobileElements checks if the value of front can be moved as in move constructors, not as in 'can be moved to a different spot in the range'. I will admit the name does little to unconfuse this point. -- Simen
Jan 29 2018
On Monday, 29 January 2018 at 12:10:16 UTC, Simen Kjærås wrote:Consider this case: immutable(int)[] a = [1,2,3]; immutable(int)* b = &a[1]; You're free to slice the array or pop elements off its front or back, but if you change the order of elements, the value that b points to will change. D does not allow this. You can create a new array with the elements moved into the locations you want, and with another layer of indirections (int*[]) you can move the elements of the array without mutating the values.Ooh my... yes I can understand that. And I guess immutable is implicitly convertible to const and then if you can move const stuff then you have that problem. From the FAQ, my general understanding is that const is there as a bridge from immutable to mutable as a promise that the memory under this symbol will not be altered by that reference. While experimenting a bit I came across this though which, currently, I do understand that value of struct Int { const int value; } void main() { Int i0 = Int(0); Int i1 = Int(1); Int i2 = Int(2); Int[3] arr0 = [i0, i1, i2]; Int[] arr1; arr1 ~= i0; arr1 ~= i1; arr1 ~= i2; arr1[0] = i0; // nope, Error: cannot modify struct arr1[0] Int with immutable members } So if a struct has a struct that has any member const, then effectively that whole struct is a const (well immutable it seems, so not even const)? Is that really correct? What is this sorcery? If yes I find it very surprising, but I don't think I'm very clear on what D is trying to solve with const ... yet. I find const a little hard to swallow so far. From what I understand, it's good for function parameters, and as a function attribute for functions that return temporaries, except don't use it with ranges (i.e. don't put it on front). Actually not sure about function attributes on second thought. So... function parameters and local variables that you know won't change is what I'm going to go with for now.I'm not following here :s If value of front can be move constructed, then something can take it's place no? Thank you!Also hasMobileElements!(const int[]) is true, so that means I can move elements around right?hasMobileElements checks if the value of front can be moved as in move constructors, not as in 'can be moved to a different spot in the range'. I will admit the name does little to unconfuse this point.
Jan 30 2018
On 01/30/2018 12:17 AM, aliak wrote:So if a struct has a struct that has any member const, then effectively that whole struct is a const (well immutable it seems, so not even const)?No, it cannot be assigned but the other parts of the object can be mutated.Is that really correct? What is this sorcery? If yes I find it very surprising, but I don't think I'm very clear on what D is trying to solve with const ... yet.It's the same with C++: A type with a const member cannot have a compiler-generated assignment operator.I find const a little hard to swallow so far. From what I understand, it's good for function parameters, and as a function attribute for functions that return temporaries, except don't use it with ranges (i.e. don't put it on front). Actually not sure about function attributes on second thought. So... function parameters and local variables that you know won't change is what I'm going to go with for now.'const' as a member function attribute is meaningful: It makes the implicit 'this' parameter const. Same as a function parameter attribute in that regard.I'm not happy that I can answer that question. At least I opened a bug about some part of the confusion recently. :) https://issues.dlang.org/show_bug.cgi?id=18036 AliI'm not following here :s If value of front can be move constructed, then something can take it's place no?Also hasMobileElements!(const int[]) is true, so that means I can move elements around right?hasMobileElements checks if the value of front can be moved as in move constructors, not as in 'can be moved to a different spot in the range'. I will admit the name does little to unconfuse this point.
Jan 30 2018
On Tuesday, 30 January 2018 at 09:51:18 UTC, Ali Çehreli wrote:Ok, that made it obvious :)[...]is trying to[...]It's the same with C++: A type with a const member cannot have a compiler-generated assignment operator.'const' as a member function attribute is meaningful: It makes the implicit 'this' parameter const. Same as a function parameter attribute in that regard.ah, ok. Gotcha, thanks again, Ali.I can[...]as in move[...]the[...]point.[...][...]constructed,[...]I'm not happy that I can answer that question. At least I opened a bug about some part of the confusion recently. :) https://issues.dlang.org/show_bug.cgi?id=18036 Ali
Jan 30 2018
On Monday, 29 January 2018 at 11:36:26 UTC, aliak wrote:On Monday, 29 January 2018 at 06:46:26 UTC, Ali Çehreli wrote:Not too hard to fix: https://github.com/dlang/phobos/pull/6090I 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)); }Haha awesome! Yes that's much better :) Note to self: learn to scroll down in the docs to find other definitions. I was convinced that remove only acted on indices :p
Jan 29 2018
On Monday, 29 January 2018 at 13:55:04 UTC, Seb wrote:On Monday, 29 January 2018 at 11:36:26 UTC, aliak wrote:Nice! :DOn Monday, 29 January 2018 at 06:46:26 UTC, Ali Çehreli wrote:Not too hard to fix: https://github.com/dlang/phobos/pull/6090I 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)); }Haha awesome! Yes that's much better :) Note to self: learn to scroll down in the docs to find other definitions. I was convinced that remove only acted on indices :p
Jan 30 2018