Комментарии 11
Потому, что эффективно реализовать yield в компилируемом языке программирования не так просто. Иначе бы yield давно уже был в C++. Особенно непросто учесть случаи вызова yield в рекурсивных функциях (а это может использоваться например при рекурсивном обходе директорий).
Ну так-то co_yield
уже года 4 есть в C++, а в C++23 добавили к нему некоторую библиотечную обвязку, например, std::generator
, который превращает корутину с этим co_yield
в нечто итерируемое. Правда, @Kelbon скажет (и покажет), что генератор можно было бы сделать и получше, но это детали. Если не нравится стандартный генератор, то можно написать свой, для этого в языке все есть.
в дереве где всего одно значение это не так впечатляюще выглядит, но с директориями это классический сценарий для рекурсивного генератора
// псевдокод не обрабатывающий много чего
generator<file_entry> directory_recursive(filesystem::path p) {
for (auto&& entry : entriees_of(p)) {
if (entry.is_direectory())
co_yield elements_of(directory_recursive(entry.path));
else
co_yild entry;
}
}
Да, появление std:generator
в C++ я как-то пропустил.
Но судя по страничке Compiler support for C++23 std::generator
на данный момент поддерживается только одним компилятором: GCC 14-й версии, который вышел совсем недавно — 07.05.2024.
И скорее всего, реализована эта штука очень неоптимально.
Я написал простейший генератор, который производит целые числа из заданного диапазона:
std::generator<int> range_generator(int start, int stop)
{
for (int i = start; i < stop; i++)
co_yield i;
}
Не берусь судить об эффективности сгенерированного компилятором кода, но выглядит страшно.
Вообще, co_yield
— это же про асинхронный код. И будет ли co_yield
когда-нибудь оптимизирован C++-компиляторами для синхронных генераторов — большой вопрос.
Хорошая классификация итераторов, спасибо.
Наверное после разбора разных схем и отличий вашей схемы от с# и D можно расширить табличку из слайда ещё одной колонкой, start. Start у всех будет совпадать с done? кроме 11l. Концептуально табличка хороша.
С++ итераторы у вас реализованы некорректно- begin() и end() должны возвращать одинаковый тип, ни один стандартный алгоритм из <algorithm> не будет работать с вашими итераторами.
И это кстати общий случай который 11l ломает- для итераторов в таком стиле каждый алгоритм требует быть определённым для пустого begin() - не удобно, раздуваем итератор (т.к optional теперь деталь реализации итератора)
Касательно реализации итератора по строкам - во первых странно строить новые типы итераторов на базе iostream который сам итератор и понятно что получается сложно местами. has_next можно убрать если проверять поток на окончание.
Из C# итератора вы выкинули главное его свойство - ленивость(создание итератора не запускает чтение данных)
С итерацией по директориям вы тоже перемудрили.
Ну и итераторы это то где как раз уместно оптимизировать производительность вплоть до инструкций.
В общем тема интересная, но над содержанием и выводами надо критически поработать.
var it = collection.__iter__();
while (true) {
try {
let current = it.__next__();
...current...
}
catch (StopIteration) {
break;
}
}
Для начала отступы а не скобки. Далее что за var и let, это не жс. Это не питон короче
А так если нет элементов none
while (current := next(it, None)) is not None:
...
Это не питон короче
Это псевдокод.
Или как вы предлагаете следовало поступить?
Код обхода итератора всегда писать на том языке программирования, к которому он относится?
Не все знакомы с синтаксисом Python или того же Rust.
Поэтому я выбрал что-то среднее между C++/C# и JavaScript, т.к. синтаксис Си-подобных языков знаком большинству программистов.
Ещё забыли модель итераторов Lua.
В поисках оптимальной модели итераторов