Как мне вернуться из функции внутри лямбды?
рассмотрим следующий код игрушки, чтобы определить, содержит ли диапазон элемент:
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;
}