Как мне вернуться из функции внутри лямбды?

рассмотрим следующий код игрушки, чтобы определить, содержит ли диапазон элемент:

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;
}

(Да, я знаю, в стандартной библиотеке уже есть прекрасные алгоритмы, это не главное.)

как бы я написал то же самое с for_each и лямбды? Следующее не работает...

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;
}

...потому что это вернется только из лямбды, а не из функции.

Я должен бросить исключение, чтобы выйти из лямбда? Опять же, есть, вероятно, дюжина лучших решений этой конкретной проблемы, которые вообще не связаны с лямбдами, но это не то, о чем я прошу.

7 ответов


как бы я написал то же самое с for_each и лямбды?

вы можете (оставляя в стороне исключения). Ваша функция не изоморфна циклу for-each (= своего рода отображению), это так просто.

вместо этого ваша функция описывается уменьшением, поэтому, если вы хотите использовать функции более высокого порядка для ее замены, используйте уменьшение, а не карту.

если C++ had соответствующее, общего назначения!--2--> затем ваш алгоритм будет выглядеть следующим образом:

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;
    });
}

конечно, это только выходит рано, если сокращение правильно специализировано для булевых значений результата, чтобы короткое замыкание.

увы, C++ не предлагает такой функциональности, насколько я вижу. Есть accumulate но это не будет короткое замыкание (он не может – C++ не знает, что операция внутри лямбда закорочена, и она не реализована рекурсивно.)


std::for_each это не алгоритм, который вы должны использовать, если вы хотите завершить цикл досрочно. Кажется, вы хотите std::find_if или что-то подобное. Вы должны использовать алгоритм, наиболее подходящий для вашей задачи, а не только тот, с которым вы знакомы.


Если вы действительно, действительно, действительно должен" вернуться " из алгоритма рано, вы можете -

предупреждение: то, что следует, действительно,действительно плохая идея, и вы должны практически никогда не делайте этого. В самом деле, глядя на код может расплавить ваше лицо. Вы были предупреждены!

исключение:

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;
}

лямбды-неправильный уровень абстракции, потому что они ведут себя в основном как функции-по крайней мере, когда дело доходит до управления потоком, что здесь имеет значение. Вы не хотите что-то, как выражена как функция (или процедура в процедурном программировании), которые могут в C++ только либо сразу вернуться или бросить исключение. Любая попытка подорвать это поведение, по моему мнению, должна считаться патологией-или, по крайней мере, не должна маскироваться под процедура.

для более точного управления потоком выполнения, что-то вроде сопрограмм может быть лучше подходит уровень абстракции и/или примитивные. Тем не менее, я боюсь, что конечный результат не будет выглядеть как использование std::for_each.


используйте пользовательский алгоритм:

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;
}

хорошо, это на самом деле std::all_of, но вы получаете идею. (См. "ответ на сокращение"). Если вашей функции нужно вернуть какой-то тип, вы можете использовать какой-то тип варианта :

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

или

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

см. соответствующие типы Haskell. Вы можете использовать C++01 "неограниченное объединение", чтобы реализовать это чисто.

чистый способ сделать нелокальный выход, использует продолжения, но у вас их нет С.++


использовать 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;
}

в этом контексте лямбда так же, как и любая другая функция, которая вызывается из данной функции contains2(). Возврат из другой функции не означает, что вы возвращаетесь из данной функции. Таким образом, это не возможно и это то, как дизайн должен идти.

для скороговорки, как данный пример, бросая исключение ненужных накладных расходов. Я бы поставил bool переменная внутри лямбды вместо return (а также begin = end;). Это bool может быть проверено на возврат из заданной функции contains2().


как вы и другие указали for_each не является правильным алгоритмом для использования здесь. Нет никакого способа вырваться из for_each цикл-за исключением исключения (каламбур) - вы должны полностью его выполнить.

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; 
}