Bug 4535 – std.range could have a takeWhile!pred(range) function

Status
RESOLVED
Resolution
WONTFIX
Severity
enhancement
Priority
P2
Component
phobos
Product
D
Version
D2
Platform
All
OS
All
Creation time
2010-07-30T02:49:25Z
Last change time
2022-08-31T09:27:18Z
Keywords
bootcamp
Assigned to
No Owner
Creator
Philippe Sigaud

Comments

Comment #0 by philippe.sigaud — 2010-07-30T02:49:25Z
takeWhile!predicate(range) is a very useful function, that lazily produces elements of a range, as long as a predicate holds for the produced elements. Usage example: ---- auto small = takeWhile!"a<5"([0,1,2,3,4,5,6,7,6,5,4,3,2,1,0]); assert(equal(small, [0,1,2,3,4,5]); ---- That's different from filter!predicate(range) that would return _all_ elements in the range that satisfy the predicate. Possible code for this follow: import std.functional: unaryFun; import std.range; struct TakeWhile(alias pred, R) if (isInputRange!R && is(typeof(!unaryFun!pred(ElementType!R.init)) == bool)) { R _range; bool empty() @property { return _range.empty || !(unaryFun!pred(_range.front)); } ElementType!R front() @property { return _range.front; } TakeWhile save() @property { return this; } void popFront() { _range.popFront; } } TakeWhile!(pred, R) takeWhile(alias pred, R)(R range) if (isInputRange!R) { return TakeWhile!(pred,R)(range); } unittest { auto arr = [0,1,2,3,4,3,2,1,0]; // With 'string functions' assert(equal( takeWhile!"a<3"(arr), [0,1,2])); // standard case assert(equal( takeWhile!"a<10"(arr), arr)); // predicate true for all elements assert(takeWhile!"a<0"(arr).empty); // predicate false from the beginning assert(takeWhile!"a!=0"(arr).empty); // predicate false for the first element // With a standard function bool foo(int i) { return i != 4;} assert(equal( takeWhile!foo(arr), [0,1,2,3])); }
Comment #1 by dsimcha — 2010-08-14T18:32:44Z
Doesn't std.algorithm.until pretty much do what you want?
Comment #2 by issues.dlang — 2010-08-14T18:45:43Z
I'd say that it mostly does, but it's not generic enough. until() has the concept of a sentinel, while takeWhile() is based entirely on a predicate. I'm not quite sure how their implementations would be different, but I'd expect takeWhile() to be at least slightly more efficient (though perhaps negligably so) than until(). I think that it would be beneficial to have the more generic takeWhile(); if nothing else, the intent is clearer. Of course, truth be told, I'm not a huge fan of until() anyway. I really think that it should be more like find() except return what's before what you're looking for rather than starting with it like find() does.
Comment #3 by philippe.sigaud — 2010-08-19T15:46:32Z
(In reply to comment #1) > Doesn't std.algorithm.until pretty much do what you want? Hmm. I see until() is templated on the predicate. I guess most of the time, I can rewrite my predicates to use until. auto t = takeWhile!"a*a>10"(someValues); auto u = until!"a*a>b"(10, someValues); // hey, b is 10, a will be elements from someValues. Is that clear? I personally find takeWhile to be more readable. Maybe I'm biased due to my using it in other languages? Also, it's cumbersome for no-arg functions: // takes lines as long as they are not empty auto t = takeWhile!"!a.empty"(file.byLine); auto u = until!"!a.empty"(dummy, file.byLine); auto t = takeWile!externalUnaryPredicate(someValues); auto u = until!externalUnaryPredicate(??, someValues); // How do I do that? Philippe
Comment #4 by timon.gehr — 2011-06-04T13:36:46Z
*** Issue 6104 has been marked as a duplicate of this issue. ***
Comment #5 by timon.gehr — 2017-07-11T07:45:15Z
This enhancement requests is not "INVALID".
Comment #6 by issues.dlang — 2017-07-11T08:40:39Z
(In reply to timon.gehr from comment #5) > This enhancement requests is not "INVALID". Closing it as "won't fix" would be more appropriate. We already have until, which does what takeWhile would do - it just has an arguably worse name. Is there something about until that does not fulfill what takeWhile would do? While I don't think that it existed when this issue was opened, there is now an overload for until that does not take a sentinel and AFAIK does exactly what takeWhile would do.
Comment #7 by timon.gehr — 2017-07-11T08:55:35Z
(In reply to Jonathan M Davis from comment #6) > (In reply to timon.gehr from comment #5) > > This enhancement requests is not "INVALID". > > Closing it as "won't fix" would be more appropriate. Perhaps. There is nothing about the suggestion that is "invalid" though. > We already have until, > which does what takeWhile would do - it just has an arguably worse name. Is > there something about until that does not fulfill what takeWhile would do? In many (most?) circumstances it is just silly to have to specify a condition to detect the first element that you do /not/ want included in your range. There is a reason 'filter' keeps the elements for which the predicate evaluates to 'true' and not those for which it evaluates to 'false'. > While I don't think that it existed when this issue was opened, there is now > an overload for until that does not take a sentinel and AFAIK does exactly > what takeWhile would do. I guess one way to use it is to create one's own takeWhile function.
Comment #8 by issues.dlang — 2017-07-11T10:02:24Z
(In reply to timon.gehr from comment #7) > (In reply to Jonathan M Davis from comment #6) > > We already have until, > > which does what takeWhile would do - it just has an arguably worse name. Is > > there something about until that does not fulfill what takeWhile would do? > > In many (most?) circumstances it is just silly to have to specify a > condition to detect the first element that you do /not/ want included in > your range. There is a reason 'filter' keeps the elements for which the > predicate evaluates to 'true' and not those for which it evaluates to > 'false'. True, in may circumstances, having to negate your predicate is annoying, but when I tried to get takeWhile and dropWhile added on that basis several years ago, Andrei vetoed it, because it was trivial to just negate the predicate, and he's right. It may be a bit annoying, but it is trivial. There's still some value in takeWhile on the basis of user friendliness if you have a predicate that would need to be negated to be used with until, and some folks do go looking for takeWhile and fail to find until (though until's docs do now mention that it's essentially takeWhile), but we do generally try to avoid adding extremely simple functions to Phobos, and if takeWhile or dropWhile were added, they would almost certainly be added as simple wrappers around until and find respectively. So, unless someone can convince Andrei that having to negate the predicate is onerous enough to merit adding the opposite functions and/or the negated versions are more common than what we have, I think that it's pretty clear that neither takeWhile nor dropWhile are going to be added, because we already have the functionality that they provide, even if it does require that you sometimes negate your predicate.
Comment #9 by timon.gehr — 2017-07-12T07:45:29Z
(In reply to Jonathan M Davis from comment #8) > (In reply to timon.gehr from comment #7) > > (In reply to Jonathan M Davis from comment #6) > > > We already have until, > > > which does what takeWhile would do - it just has an arguably worse name. Is > > > there something about until that does not fulfill what takeWhile would do? > > > > In many (most?) circumstances it is just silly to have to specify a > > condition to detect the first element that you do /not/ want included in > > your range. There is a reason 'filter' keeps the elements for which the > > predicate evaluates to 'true' and not those for which it evaluates to > > 'false'. > > True, in may circumstances, having to negate your predicate is annoying, but > when I tried to get takeWhile and dropWhile added on that basis several > years ago, Andrei vetoed it, because it was trivial to just negate the > predicate, and he's right. It may be a bit annoying, but it is trivial. > ... I'm not using Phobos in order to get annoyed by trivialities, and neither are the people who will need to read my code. It is not inadvisable to not condone double-negation in text for which we want to avoid that it does not fail to be hard to digest. > There's still some value in takeWhile on the basis of user friendliness if > you have a predicate that would need to be negated to be used with until, > and some folks do go looking for takeWhile and fail to find until (though > until's docs do now mention that it's essentially takeWhile), but we do > generally try to avoid adding extremely simple functions to Phobos, and if > takeWhile or dropWhile were added, they would almost certainly be added as > simple wrappers around until and find respectively. > ... User friendliness, reader friendliness, name standardization. > So, unless someone can convince Andrei that having to negate the predicate > is onerous enough to merit adding the opposite functions and/or the negated > versions are more common than what we have, It is controversial that takeWhile is more commonly useful that until? > I think that it's pretty clear > that neither takeWhile nor dropWhile are going to be added, because we > already have the functionality that they provide, even if it does require > that you sometimes negate your predicate. It is wrong to think in terms of "functionality" for combiners used in a functional program. Every additional negation makes a one-liner harder to understand even if it inconveniences its construction in a minor (but still, unnecesssary) way.
Comment #10 by razvan.nitu1305 — 2017-07-12T11:28:40Z
PR : https://github.com/dlang/phobos/pull/5563 We should either close this or work on the PR.
Comment #11 by andrei — 2017-07-17T16:25:38Z
I don't think it's wise to add HOFs of which predicates just negate that of other HOFs. I'm convinced that any programmer can be reasonably expected to figure negation out without problems, otherwise we're liable to experiments a la Perl/Ruby "unless" as a negation of "if". My understanding is that the usefulness of such is still a matter of debate and style differences within the respective communities. I'll close this, please reopen if there are qualitatively new arguments.
Comment #12 by black80 — 2019-07-14T14:42:46Z
(In reply to Andrei Alexandrescu from comment #11) > I don't think it's wise to add HOFs of which predicates just negate that of > other HOFs. I'm convinced that any programmer can be reasonably expected to > figure negation out without problems, otherwise we're liable to experiments > a la Perl/Ruby "unless" as a negation of "if". My understanding is that the > usefulness of such is still a matter of debate and style differences within > the respective communities. > > I'll close this, please reopen if there are qualitatively new arguments. 1) word "until" is doubt word for me (I am non English native) when I hear "until noon" I am in doubt cuz it means "they will care something before noon" or "when noon will come then they take care of it" 2) until accepts sentinel that don't needed for explicit predicate in most time 3) with explicit predicate that accepts 2 args it adds another doubt about arg order: what 1st arg - element or sentinel? for example I want sequence of fibonacci numbers less than 100 fibs.until!((a,b) => !(a<b))( 100 ).each!writeln; // ^ element is 1st or 2nd? lets go to look docs // ^ ^ when I have explicit predicate why I need sentinel as arg at all? I can put it as literal to predicate // ^ condition is negate of my wish. yes I can negate condition for IF/FOR/WHILE cases but with doubts for UNTIL its make me crazy. I should write some test in REPL just to make sure that I am not failed with such simple condition. why that? totally its too complicated for such simple thing: fibs.takeWhile!(e => e<100).each!writeln; or even shorter fibs.takeWhile!"e<100".each!writeln; and we have already take, takeOne, takeNone, takeExactly. why do not just to add this one to set of same named algos? so please add takeWhile/skipWhile algos to library cuz its clear and shorter and without_unnecessary_negate (haha?) and without_unclear (haha again?) args order =========================== another thing that I want to tell on the example of next comment > Jonathan M Davis > Closing it as "won't fix" would be more appropriate. We already have until, which does what takeWhile would do - it just has an arguably worse name. Is there something about until that does not fulfill what takeWhile would do? While I don't think that it existed when this issue was opened, there is now an overload for until that does not take a sentinel and AFAIK does exactly what takeWhile would do. well, last sentence is wrong: "until" jast boomboom brains instead "takeWhile". see my arguments above. imo u have some preset that makes you decide that only one name should exists for things that somehow likely. well, turn around, u can see cars, people, things they are all same but they are all are different. its not a sin to have aliases for likely things or functions. people comes to D from other langs, they fill more comfort with names to which they are accustomed. and takeWhile and skipWhile is most useful names to enumerate elements for true predicate. (python, .net...) when u try find method like for such task i am sure u will type "dlang take while" not "dlang until" cuz "until" is any f*king blog word. what u expect to find with it? if u like "until" then use it, but please don't prohibit things that more comfortable to others. and "until" and "takeWhile" and "skipWhile" and "drop" can coexists in one library if u free your brain from some inexplicable preset. just add aliases with right predicates to most useful algos and links such aliases to each other in documentation when they are 100% same. "until" and "takeWhile" is not same for 100% probably only for -60% (minus for negate condition) PS "skip" is more clear/pure name for "drop" cuz we are skipping element not droping it to somewhere (another range or ground floor) maybe for English native is same thing, but I dont know 100 meanings for one simple word. PS2 "until" is still useful for non literal sentinel or with default implicit predicate
Comment #13 by black80 — 2019-07-14T16:36:06Z
(In reply to KnightMare from comment #12) my version of wrapping "until" to "takeWhile": auto takeWhile( alias F, R )( R r ) { struct NR { bool isEmpty = false; this( bool e ) { isEmpty = e; } bool empty() { return isEmpty; } auto front() { return r.front; } void popFront() { if (isEmpty) return; r.popFront; isEmpty = isEmpty || !F( r.front); } } return NR( r.empty ); } I was hinted at IRC channel that it can be simpler: > auto takeWhile( alias pred, A...)( A a) { return until!( not!pred)( a); } and no string version yet like .takeWhile!"n<7". do u see that how many code for not familiar users with metaprogramming in D? u should understand that most of programmers don't know every aspect in D, they try to use same things that they are used before same way as before, they have some expectations, they want more clearness without new strong headache that exists in learning of non-trivail programming language. so, adding known aliases and simpler versions of algos to Phobos u make simpler life for other non-meta-gurus. imo nobody will search "until" for enumerating ranges when they are look at 4 "take..." and don't see "take with predicate".. yes, they have some thoughts that should exists something with predicate - it is so expected.. but it new research for nothing when they are researching cure for cancer for example.
Comment #14 by razvan.nitu1305 — 2022-08-31T09:02:28Z
This has been discussed a number of times and the conclusion is that this won't be "fixed". Anyone that is not satisfied with until can trivially implement a takeWhile wrapper.
Comment #15 by timon.gehr — 2022-08-31T09:27:18Z
I am not satisfied with the proposed workaround, because this function should obviously be standardized. Please don't suggest that I should be "satisfied" or that my concern is "trivial". It's not respectful. Thanks.