Comments 64
видимо, о for_each Вы не слышали…
-3
И слышал, и пользовался. Как по мне более громоздко и менее читаемо. Кроме того не пойму как std::for_each относится к перебору индексов.
+4
но зато это стандарт, а не велосипед.
-9
Как по мне в С++11 for_each абсолютно не нужен с учетом range-based for. Кроме того не могу представить как с помощью for_each сделать проход по индексам.
+4
Для примера:
Быдлокод, но вполне понятно. Или я не понял сути вопроса?
PS: Черт, ниже уже есть похожий вариант :)
std::deque<std::string> list;
...
for (auto item : list)
{
printf("%s\n", items.c_str());
}
Быдлокод, но вполне понятно. Или я не понял сути вопроса?
PS: Черт, ниже уже есть похожий вариант :)
+1
Только лучше
чтобы избежать ненужного копирования
for (auto const & item: list)
чтобы избежать ненужного копирования
+3
Вы как раз написали почему я назвал for_each аттавизмом — Вы написали range-based for-loop. Но бывают случает когда нам нужно перебирать не элементы, а индексы (например, если мы обрабатываем не одну коллекцию в теле цикла). Для того, тобы написать такой цикл по иденксам но в формате range-based for-loop, и был написан код из статьи.
0
UFO just landed and posted this here
Зачем из одного языка делать другой?
-4
Если итераторы сравнивать не по значению, а по номеру шага, то не будет проблем с вещественными числами.
А ещё мне очень не нравится это: m_value += m_range->step();
Т.к. известны начальное значение, инкремент и номер шага, то лучше сделать через умножение. От плюсования погрешность накапливается неприличными темпами.
А ещё мне очень не нравится это: m_value += m_range->step();
Т.к. известны начальное значение, инкремент и номер шага, то лучше сделать через умножение. От плюсования погрешность накапливается неприличными темпами.
+1
Первая реализация и была основана на номере шага. Но тогда диапазон органичен максимальным значением типа size_t. Правда в реальной жизни вряд ли кто-то будет перебирать такие диапазоны.
0
У вас диапазон в любом случае будет ограничен максимальным значением size_t. В этом же суть этого типа.
+1
for-each, мне кажется, здесь иррелевантен. Первичным является как раз понятие интервала. Тут один товарищ как раз работает над разработкой интервалов для следующего стандарта :)
+1
Да, я читал эти две статьи. Но там понятие интервала очень обобщенное.
0
В библиотеке range-v3 довольно много предопределённых диапазонов и операций над ними. Смотрите (пока неполный) мануал тут. Для вашего случая интересны
Пример:
Ну и ещё эту бублиотеку полезно постмотреть потому, что она пути в стандарт (правда пока не ясно, когда).
view::ints
и view::iota
. Готовых интервалов с произвольным шагом нету.Пример:
// prints numbers 1...10
for (auto i: view::ints(1, 11))
std::cout << i << std::endl;
Ну и ещё эту бублиотеку полезно постмотреть потому, что она пути в стандарт (правда пока не ясно, когда).
+1
Спасибо!
0
Ещё можно глянуть обсуждение по теме тут
+1
Рекомендую посмотреть на LINQ из .NET Framework. Я вроде делал генераторы ренджей в имплементации клона на C++ github.com/k06a/boolinq
-1
Да, в самом конце файла github.com/k06a/boolinq/blob/master/include/boolinq/boolinq.h
-1
А зачем вам вообще индексы в таких высокоуровневых языках как C++ и python?
-3
Разные потребности бывают, иногда и на очень низкий уровень приходится опуститься, до всяких intrinsic'ов и прочего, там и цикл по индексам может пригодиться, но старый-добрый for не кошере, а решение с мета-оберткой и range-based for хорошо оптимизируется и более читаемо. Например, вариант приведенный мною ниже, в GCC/CLang последних версий с -O2/-O3 компилируется в машинный код, эквивалентный коду, получаемому из hand-written for по индексам.
+1
А с каких пор C++ стоит в одном ряду с питоном в качестве высокоуровнего языка? По мне так он ближе к среднему уровню.
-1
Я использую нечто такое в своих личных и рабочих проектах (уже неактуальна версия, не library-качества):
gist.github.com/DieHertz/f83b33ffe33e1c07abfc
gist.github.com/DieHertz/f83b33ffe33e1c07abfc
0
Намного лучше в перле:
for (0..10) {
print
}
Ну вот зачем куча лишних слов — in, range,… Без лишнего мусора элегантней.
for (0..10) {
}
Ну вот зачем куча лишних слов — in, range,… Без лишнего мусора элегантней.
0
Считается, что чем больше код похож на обычное предложение, тем лучше воспринимается человеком
0
Какое предолжение обычнее, удобнее, проще:
от нуля до десяти делаем…
в диапазоне ноль, десять делаем…
неговоря уж о часто ненужном индексе, который в перле можно опустить, а в питоне — вынь да полож.
в обычных предложениях ненужные вещи опускают.
от нуля до десяти делаем…
в диапазоне ноль, десять делаем…
неговоря уж о часто ненужном индексе, который в перле можно опустить, а в питоне — вынь да полож.
в обычных предложениях ненужные вещи опускают.
0
неговоря уж о часто ненужном индексе, который в перле можно опустить, а в питоне — вынь да полож.В Python (и не только) для таких целей служит подчерк:
for _ in range(0, 10):
pass
Насчёт формулировок — дело привычки. Мне поначалу вариант на Perl чересур упрощённым и вследствие непонятным, но после некоторых раздумий согласен, так удобнее. Perl вообще очень богат на различные человеко-ориентированные конструкции:
chdir '/usr/spool/news' or die "Can't cd to spool: $!\n";
print 'ok' if chdir '/tmp';
Python перенял часть, но всё-таки все ему не нужны — идеология «есть только один способ сделать это» неслабо разнится с идеологией «есть более чем один способ сделать это».
0
В статье я рассматривал случай когда индекс необходим внутри тела цикла.
Если же индекс ненужен (я так понимаю именно это Вы имели под вохможностью его опустить), то вариант:
для меня выглядел бы лучше.
Если же индекс ненужен (я так понимаю именно это Вы имели под вохможностью его опустить), то вариант:
loop(number_of_times) {
do_stuff()
}
для меня выглядел бы лучше.
0
В perl'е индекс доступен через
<a href="http://perldoc.perl.org/perlvar.html#General-Variables">$_</a>
— и это одна из вещей, которая превращает программу в ребус. Где эта переменная возникла? Когда исчезнет? Как меняется? Какая у неё области видимости и как это зависит от версии perl'а? Это ж просто пьесня — без работы не останетесь!0
Ну тогда тоже самое на Bash
echo {1..10}
0
А что насчет производительности по сравнению с использованием оригинального синтаксиса? Какой overhead?
0
Для простого случая gcc выдаёт практически идентичный код.
#include <boost/range/irange.hpp>
void do_the_thing(int);
void oldschool(int a, int b)
{
for (auto i = a; i < b; ++i)
do_the_thing(i);
}
void ranges(int a, int b)
{
for (auto i : boost::irange(a, b))
do_the_thing(i);
}
$ g++-4.9.2 -std=c++14 -O3 -DNDEBUG -c range.cpp
$ objdump -dC range.o
range.o: формат файла elf64-x86-64
Дизассемблирование раздела .text:
0000000000000000 <oldschool(int, int)>:
0: 55 push %rbp
1: 53 push %rbx
2: 89 f5 mov %esi,%ebp
4: 89 fb mov %edi,%ebx
6: 48 83 ec 08 sub $0x8,%rsp
a: 39 f7 cmp %esi,%edi
c: 7d 10 jge 1e <oldschool(int, int)+0x1e>
e: 66 90 xchg %ax,%ax
10: 89 df mov %ebx,%edi
12: 83 c3 01 add $0x1,%ebx
15: e8 00 00 00 00 callq 1a <oldschool(int, int)+0x1a>
1a: 39 eb cmp %ebp,%ebx
1c: 75 f2 jne 10 <oldschool(int, int)+0x10>
1e: 48 83 c4 08 add $0x8,%rsp
22: 5b pop %rbx
23: 5d pop %rbp
24: c3 retq
25: 66 66 2e 0f 1f 84 00 data32 nopw %cs:0x0(%rax,%rax,1)
2c: 00 00 00 00
0000000000000030 <ranges(int, int)>:
30: 55 push %rbp
31: 53 push %rbx
32: 89 f5 mov %esi,%ebp
34: 89 fb mov %edi,%ebx
36: 48 83 ec 08 sub $0x8,%rsp
3a: 39 f7 cmp %esi,%edi
3c: 74 10 je 4e <ranges(int, int)+0x1e>
3e: 66 90 xchg %ax,%ax
40: 89 df mov %ebx,%edi
42: 83 c3 01 add $0x1,%ebx
45: e8 00 00 00 00 callq 4a <ranges(int, int)+0x1a>
4a: 39 dd cmp %ebx,%ebp
4c: 75 f2 jne 40 <ranges(int, int)+0x10>
4e: 48 83 c4 08 add $0x8,%rsp
52: 5b pop %rbx
53: 5d pop %rbp
54: c3 retq
Насчет сложных случаев не уверен, можно ли сочинить такой, что gcc решит это всё не инлайнить.
+1
Куда всё катится! Как можно упростить неупрощаемое? Во сколько раз больше нужно машинных инструкций с ненужными умножениями на исполнение вот таких упрощений? И на сколько такое упрощение дает выйгрышь девелоперу (одна строчка простого кода против одной строчки простого кода)?
-2
У первого варианта есть ещё один серьёзный недостаток: возможность наличия сайд-эффектов в max
+1
А я использую примерно такой же макрос FOR_S (var, min, maxplus).
Буква s означает size_t. Правда, с другой целью — уменьшить вероятность ошибки.
Буква s означает size_t. Правда, с другой целью — уменьшить вероятность ошибки.
0
То же самое пишется куда как лаконичнее с использованием моего $generator/$yield. И не только на ranges:
И использование
Сама статья с имплементацией $generator/$yield — 16 строк .h файл.
include "generator.h"
$generator(descent)
{
// place for all variables used in the generator
int i; // our counter
// place the constructor of our generator, e.g.
// descent(int minv, int maxv) {...}
// from $emit to $stop is a body of our generator:
$emit(int) // will emit int values. Start of body of the generator.
for (i = 10; i > 0; --i)
$yield(i); // a.k.a. yield in Python,
// returns next number in [1..10], reversed.
$stop; // stop, end of sequence. End of body of the generator.
};
И использование
int main(int argc, char* argv[])
{
descent gen;
for(int n; gen(n);) // "get next" generator invocation
printf("next number is %d\n", n);
return 0;
}
Сама статья с имплементацией $generator/$yield — 16 строк .h файл.
-1
Выше уже писали преимущество мета-обертки — очень часто генерируется код без дополнительных расходов, как-будто обычный for и был написан изначально. Ваш код будет так же компилироваться?
Кроме того отладка кода с макросами — это жесть.
Ну и читаемость… Лично я сходу не смог разобраться что это за новый оператор $ появился в с++ и с++ ли это вообще.
Кроме того отладка кода с макросами — это жесть.
Ну и читаемость… Лично я сходу не смог разобраться что это за новый оператор $ появился в с++ и с++ ли это вообще.
+1
Прикольная штука. Только зачем у генератора все его «кишки» сделаны публичными?
0
Я в свое время не стал сильно заморачиваться, сделал примерно так:
Пример использования:
/*!
* range ( container, from, to, [ step ] );
* @returns { container<T> }
*/
template <typename Container, typename Position, typename Step = uint>
static inline Container range (
Container &container,
Position from,
Position to,
Step step = 1) noexcept
{
typename std::back_insert_iterator<Container>
it = std::back_inserter(container);
do {
*it++ = from;
}
while ((from += step) <= to);
return container;
}
;
Пример использования:
std::vector<int> vector;
for (auto value: range(vector, 1, 5)) {
std::cout << value << std::endl; // 1, 2, 3, 4, 5
}
-1
Для вещественных диапазонов есть две альтернативы итераторов
— (значение, шаг) и приблизительное сравнение по значению
— (начало, шаг, индекс) и точное сравнение по индексу — то есть, фактически, transform_iterator линейной функции поверх обычного целочисленного range.
Приблизительное сравнение плохо тем, что оно позволяет организовать путаницу между обычным полуоткрытым интервалом и закрытым.
range(0.0, 1.0, 0.2) = { 0.0, 0.2, 0.4, 0.6, 0.8 }
range(0.0, 1.0+EPS, 0.2) = { 0.0, 0.2, 0.4, 0.6, 0.8, 1.0 } — эмулировали закрытый интервал из полуоткрытого… якобы. На всех этапах поимели ошибки округления. Исходя из этого значение EPS нужно будет подобрать достаточно большим, чтобы переплюнуть сумму ошибок, но достаточно маленьким, чтобы не перескочить через следующее за концом значение.
Тогда как сделать закрытый интервал из полуоткрытого по-честному на целочисленной основе — элементарно.
Опять же, диапазон следует внутренне представлять не тройкой (начало, конец, шаг), а (начало, шаг, количество).
Раз уж всё равно end() { return begin() + size(); }
где size() { return (m_max-m_min)/m_step; }
— (значение, шаг) и приблизительное сравнение по значению
— (начало, шаг, индекс) и точное сравнение по индексу — то есть, фактически, transform_iterator линейной функции поверх обычного целочисленного range.
Приблизительное сравнение плохо тем, что оно позволяет организовать путаницу между обычным полуоткрытым интервалом и закрытым.
range(0.0, 1.0, 0.2) = { 0.0, 0.2, 0.4, 0.6, 0.8 }
range(0.0, 1.0+EPS, 0.2) = { 0.0, 0.2, 0.4, 0.6, 0.8, 1.0 } — эмулировали закрытый интервал из полуоткрытого… якобы. На всех этапах поимели ошибки округления. Исходя из этого значение EPS нужно будет подобрать достаточно большим, чтобы переплюнуть сумму ошибок, но достаточно маленьким, чтобы не перескочить через следующее за концом значение.
Тогда как сделать закрытый интервал из полуоткрытого по-честному на целочисленной основе — элементарно.
Опять же, диапазон следует внутренне представлять не тройкой (начало, конец, шаг), а (начало, шаг, количество).
Раз уж всё равно end() { return begin() + size(); }
где size() { return (m_max-m_min)/m_step; }
0
Sign up to leave a comment.
Упрощаем for-цикл по индексам: range-based версия