Thread overview
Best way to test predicate against a range
Jun 27, 2010
Jonathan M Davis
Jun 27, 2010
BCS
Jun 27, 2010
Philippe Sigaud
Jun 27, 2010
bearophile
June 27, 2010
Okay. The functions in std.algorithm are quite powerful, but sometimes you have to play around with them a bit to figure out how to do exactly what you're trying to do. Sometimes it's a matter of figuring out exactly which algorithm really does what you want, and sometimes it's a matter of figuring out how to contort one of them to do what you want.

For example, there are two functions that I'd like to be have: all() and any(). That is, I want a function which checks a predicate against a range and returns whether all elements in that range satisfy the predicate, and I want a function that checks a predicate against a range and returns whether any element satisfies the predicate. Ideally, all() would shortcut if it found even one element which didn't satisfy the predicate, and any() would shortcut if it found even one that did.

From the looks of it, canFind() is essentially any(), so that new addition to std.algorithm should deal with that. Previously, the best that I could figure out was to use find() for that and check whether the returned range was empty, which wasn't as efficient or elegant. So, that's a nice addition to phobos.

The problem is all(). I can't find any function which seems to effectively do all(). The best that I can think of is to use canFind() with a negated predicate, so if it returns true, it found an element which satisfied the negation of the predicate and therefore didn't satisfy the predicate. However, ideally, I'd be able to give a function the exact predicate that I'm looking for without having to contort the predicate to be able to use the function that I want. It feels like I'm dealing with only a || and I don't have a &&.

Is there a function in phobos which can't be used as an all(), and I'm just missing it, or is there at least a better solution than using canFind() with a negated predicate, or is canFind() with a negated predicate the best that there is at the moment?

- Jonathan M Davis


P.S. By the time that std.algorithm is completed, I suspect that it would be possible to write a small book on the myriad of ways to use it. It's extremely powerful and not really all that hard to use, but sometimes it takes a fair bit of thinking (for me at least) to figure out which function to use to do something. Hopefully I get better at it with time at least.
June 27, 2010
Hello Jonathan,

> For example, there are two functions that I'd like to be have: all()
> and any(). That is, I want a function which checks a predicate against
> a range and returns whether all elements in that range satisfy the
> predicate, and I want a function that checks a predicate against a
> range and returns whether any element satisfies the predicate.
> Ideally, all() would shortcut if it found even one element which
> didn't satisfy the predicate, and any() would shortcut if it found
> even one that did.

The trivial solution (no shortcuting) would be to map the predicate and reduce via 'and' or 'or'.

-- 
... <IXOYE><



June 27, 2010
On Sun, Jun 27, 2010 at 08:07, BCS <none@anon.com> wrote:

> Hello Jonathan,
>
>
>  For example, there are two functions that I'd like to be have: all()
>> and any(). That is, I want a function which checks a predicate against a range and returns whether all elements in that range satisfy the predicate, and I want a function that checks a predicate against a range and returns whether any element satisfies the predicate. Ideally, all() would shortcut if it found even one element which didn't satisfy the predicate, and any() would shortcut if it found even one that did.
>>
>
> The trivial solution (no shortcuting) would be to map the predicate and
> reduce via 'and' or 'or'.


Or do a new version of reduce that stops the reducing on a certain condition

reduceWhile(alias reducer, alias predicate) {... reduce using reducer, as
long as predicate holds }

That would be a nice addition to std.algo. The question is: what should test the predicate? The last returned value, the entire result being created?



As an asideJonathan, you may be interested in using some of the algorithms here:

http://www.dsource.org/projects/dranges

'all' and 'some' are in the 'predicates' module. They accept predicates of any arity (even 0!), because I wanted to test for increasing numerical ranges, like this :

auto isGrowing = all ! "a<b" (range);

Note that the fact of accepting variable-arity predicates represents 90% of the complexity, because a simple all is:

import std.functional: unaryFun;
import std.range: isInputRange;

bool all(alias predicate, R)(R range) if (isInputRange!R)
{
    foreach(elem; range)
    {
        if (!unaryFun!predicate(elem)) return false;
    }
    return true;
}
// warning : untested.

usage:

auto a = all ! "a<0" (range)

or

auto a = all ! foo (range)


Philippe


June 27, 2010
Jonathan M Davis:

> Okay. The functions in std.algorithm are quite powerful, but sometimes you have to play around with them a bit to figure out how to do exactly what you're trying to do.

Some of them need to be improved in their concept and API. Some of them are awkward to use or they are not the most handy. They are not battle-tested at all, the best group of functions is yet to be found.

Bye,
bearophile