21

Consider the following toy code to determine whether a range contains an element:

template<typename Iter, typename T>
bool contains1(Iter begin, Iter end, const T& x)
{
    for (; begin != end; ++begin)
    {
        if (*begin == x) return true;
    }
    return false;
}

(Yes I know, there are already perfectly fine algorithms in the standard library, that's not the point.)

How would I write the same thing with for_each and a lambda? The following doesn't work...

template<typename Iter, typename T>
bool contains2(Iter begin, Iter end, const T& x)
{
    std::for_each(begin, end, [&x](const T& y) {
        if (x == y) return true;
    });
    return false;
}

...because that would only return from the lambda, not from the function.

Do I have to throw an exception to get out of the lambda? Again, there are probably a dozen better solutions to this specific problem that do not involve lambdas at all, but that's not what I'm asking for.

fredoverflow
  • 237,063
  • 85
  • 359
  • 638
  • 1
    You cannot return from lambda this way. Lambda is, for compiler, another function, can be passed somewhere else. It would be rather silly to pass lambda to another method, where it's call would jump up 2 levels, wouldn't it? – nothrow Sep 03 '11 at 07:05
  • 4
    You really shouldn't use for_each if you don't want to process all elements. – Bo Persson Sep 03 '11 at 07:12
  • 1
    You can't do this. You can achieve the same effect in many other ways. Do you have a non-contrived example where it would actually be worthwhile? – Mankarse Sep 03 '11 at 07:13
  • It appears throwing an exception is the way to go, at least that's how non-local returns are implemented [in Scala](http://stackoverflow.com/questions/6915701/)... – fredoverflow Sep 03 '11 at 08:36
  • 2
    That lambda is wrong (I don't know if the compiler will pick it up and complain or not, but) the inferred type of the lambda will offer `bool operator()( const T& )` but your implementation falls out of that function without returning when `x!=y`. That being said what you want to do looks more like `find_if` than `for_each` (once the function is corrected) – David Rodríguez - dribeas Sep 03 '11 at 10:30
  • @David: The second code snippet is more or less pseudo-code. – fredoverflow Sep 03 '11 at 10:33
  • @FredOverflow: NOOOOO! Please don't suggest using an [exception for flow control](http://stackoverflow.com/q/729379/28817). That is textbook bad use of of an exception. @David has it right, use `std::find_if`. It's a little misleading semantically but better than exception abuse. – deft_code Sep 06 '11 at 15:09
  • @deft: Using an exception is by definition the worst way of exiting a lambda early, simply because *there is no other way*. If Scala does it automatically, I feel no shame when doing it manually in C++. – fredoverflow Sep 06 '11 at 15:25
  • 1
    @Fred, to be clear you are not using an exception to exit a lambda early. You are using an exception to change the flow control of `std::for_each`. Ignore the lambda-ness of the functor and assume you're passing a regular function to `for_each`. You want a function that returns up two levels, without the first level knowing about it. You are right that an exception is the only way. *But*, you should not do that. There is nothing inherently wrong with the concept, except that C/C++/algol/etc don't support such syntax. You're abusing exceptions to create a language feature. – deft_code Sep 06 '11 at 15:34
  • Don't use std::find_if either, use std::any_of. See my answer for code example. – Mark Ingram Oct 03 '11 at 15:24

7 Answers7

9

How would I write the same thing with for_each and a lambda?

You can’t (leaving aside exceptions). Your function isn’t isomorphic to a for-each loop (= kind of a mapping), it’s as simple as that.

Instead, your function is described by a reduction so if you want to use higher-order functions to replace it, use a reduction, not a map.

If C++ had an appropriate, general-purpose reduce then your algorithm would look as follows:

template<typename Iter, typename T>
bool contains2(Iter begin, Iter end, const T& x)
{
    return stdx::reduce(begin, end, [&x](const T& y, bool accumulator) {
        return accumulator or x == y;
    });
}

Of course, this only exits early if the reduction is properly specialised for boolean result values, in order to short-circuit.

Alas, C++ doesn’t offer such a functionality as far as I see. There’s accumulate but that won’t short-circuit (it can’t – C++ doesn’t know that the operation inside the lambda is short-circuited, and it isn’t implemented recursively).

Konrad Rudolph
  • 482,603
  • 120
  • 884
  • 1,141
5

std::for_each is not the algorithm you should use if you want to end the loop early. It seems you want std::find_if or something similar. You should use the algorithm that is most appropriate to your task, not just the one you're familiar with.


If you really, really, really must "return" from an algorithm early, you can-

Warning: what follows is a really, really bad idea and you should virtually never do it. Indeed, looking at the code may melt your face. You have been warned!

Throw an exception:

bool contains2(Iter begin, Iter end, const T& x)
{
  try {
    std::for_each(begin, end, [&x](const T& y) {
        if (x == y)
          throw std::runtime_error("something");
    });
  }
  catch(std::runtime_error &e) {
    return true;
  }
  return false;
}
Nicol Bolas
  • 378,677
  • 53
  • 635
  • 829
  • 9
    You're basically just rephrashing the question without the question mark. He *said* he knows that there are more appropriate `std` algorithms, and he mentioned the possibility of throwing an exception. – jalf Sep 03 '11 at 07:52
3

Use std::any_of.

template<typename Iter, typename T>
bool contains2(Iter begin, Iter end, const T& x)
{
    const bool contains = std::any_of(begin, end, [&x](const T& y)
    {
        return x == y;
    });

    return contains;
}
Mark Ingram
  • 65,792
  • 48
  • 164
  • 225
2

Lambdas are the wrong level of abstraction because they behave largely like functions -- at least when it comes to control flow, which is what matters here. You don't want something as 'encapsulated' as a function (or the procedures of procedural programming), which can in C++ only either straight return or throw an exception. Any attempt at subverting this behaviour should be considered pathological in my opinion -- or at least should not masquerade as a procedure.

For finer-grained control of the flow of execution, something like coroutines might be a better suited level of abstraction and/or primitive. Still, I'm afraid the end result would look nothing like using std::for_each.

Luc Danton
  • 33,152
  • 5
  • 66
  • 110
2

Use a custom algorithm :

template<class I, class F>
bool aborting_foreach(I first, I last, F f) {
  while(;first!=last;++first) {
    if(!f(*first))
      return false;       
  }
  return true;
}

Ok, this is in fact std::all_of but you get the idea. (See the "reduction answer"). If your function needs to return some type, you might want to use some variant type :

// Optional A value
template<class A>
class maybe {
  // ...
};

or

// Stores either a A result of a B "non local return"
template<class A, class B>
class either {
  …
};

See the corresponding Haskell types. You could use C++01 "unrestricted union" to implement this cleanly.

A clean way to do non-local exit, is using continuations but you do not have them in C++.

ysdx
  • 7,725
  • 1
  • 32
  • 44
0

In this context, lambda is just like any other function which is called from the given function contains2(). Returning from other function doesn't mean you are returning from the given function. Thus this is not possible and that's how the design should go.

For the patters like given example, throwing an exception is unnecessary overhead. I would set a bool variable inside the lambda instead of return (and also set begin = end;). This bool can be checked for returning from the given function contains2().

iammilind
  • 62,239
  • 27
  • 150
  • 297
  • Except that setting a bool means the rest of the sequence would be iterated, while throwing an exception would end the iteration. Depending on the size of the sequence, throwing an exception might likely be faster (then again, I doubt performance matters, since this is obviously just a toy "what-if" example. – jalf Sep 03 '11 at 07:36
  • @jalf, we can also set `begin = end;` inside the lambda function to avoid iterating rest of the sequence. – iammilind Sep 03 '11 at 07:40
  • You can try. But that assumes that `for_each` won't copy those iterators internally. – jalf Sep 03 '11 at 07:51
0

As you and others have pointed out for_each is not the right algorithm to use here. There is no way to break out of the for_each loop - except the exception (pun intended) - you have to run through it completely.

template<typename Iter, typename T> 
bool contains2(Iter begin, Iter end, const T& x) 
{ 
    bool tContains = false;
    std::for_each(begin, end, [&](const T& y) mutable { 
        tContains = tContains || x == y; 
    });
    return tContains; 
} 
Paul Michalik
  • 4,147
  • 14
  • 18