Comments 101
Хорошая заметка.
Однако, я пожалуй выбираю старый добрый for :)
Потому, что если даже через 5 лет, я вернусь к этому коду, я моментально пойму, что он делает. А иначе, мне придется вспоминать особенности библиотеки или что это тут какая-то лямбда функция делает. И уж тем более, сторонний человек тоже быстрее поймет, что делает простой for.
Не очень понимаю я стремление к минимализму. С одной стороны сжато и красиво, но каков практический смысл? Человек большую часть времени читает код. И, к сожалению, часто чтение и осознание сжатого кода занимает больше времени, чем если бы он был записан «влоб».
P.S. Естественно я не агитирую писать длинный код. ;)
Однако, я пожалуй выбираю старый добрый for :)
int sum = 0; for(int i = 0; i < candles.size(); i++) sum += candles[i].ClosePrice;
Потому, что если даже через 5 лет, я вернусь к этому коду, я моментально пойму, что он делает. А иначе, мне придется вспоминать особенности библиотеки или что это тут какая-то лямбда функция делает. И уж тем более, сторонний человек тоже быстрее поймет, что делает простой for.
Не очень понимаю я стремление к минимализму. С одной стороны сжато и красиво, но каков практический смысл? Человек большую часть времени читает код. И, к сожалению, часто чтение и осознание сжатого кода занимает больше времени, чем если бы он был записан «влоб».
P.S. Естественно я не агитирую писать длинный код. ;)
+10
Практический смысл в том, чтобы декларативно запрашивать данные из массива или вектора. Я не думаю что код, написанный с использованием LINQ труднее для понимания. Чем меньше кода написано — тем меньше ошибок допущено) Если человек знаком с SQL — понять код на LINQ не составит труда…
После программирования на C# — отсутствие LINQ для C++ — существенный минус. Теперь мне намного проще писать на C++…
После программирования на C# — отсутствие LINQ для C++ — существенный минус. Теперь мне намного проще писать на C++…
+12
Даже для самых присамых императивщиков несекрет, почему декларативный стиль называется декларативным, и в чем его непоколебимый плюс в отображении сути кода.
+4
Практический смысл (и огромный) в компакности кода и его читаемости. Декларативно-функциональные инструкции на LINQ выглядят куда легковеснее наборов «for» и «foreach». Я уже молчу про такие вещи, как .SelectMany().
+1
Я на днях пробовал играться с Kotlin (JVM-based язык), и был неприятно удивлени отсутствием старого доброго for-a. Поигравшись немного, понял, почему они его убрали.
В Kotlin проход по индексам осуществляется выражением
for( val index: array.indices )
таким образом, нет типовых ошибок с «i < a.size()» vs. «i <= a.size()»
Это к тому, что старый добрый for, возможно, устарел :-)
В Kotlin проход по индексам осуществляется выражением
for( val index: array.indices )
таким образом, нет типовых ошибок с «i < a.size()» vs. «i <= a.size()»
Это к тому, что старый добрый for, возможно, устарел :-)
+4
что делать при реализации чуть более сложной логики, чем проход по всем элементам массива? Скажем я хочу пройти по всем нечетным:
for(int i=1; i < size; i+=2)
for(int i=1; i < size; i+=2)
+1
Насколько я понял, там вводится механизм итераторов (алгоритмов обхода коллекций). Например, реверсивный обход делается так:
for( val index: array.indices.reverse )
Но я глубоко не вникал, так по верхушкам посмотрел…
for( val index: array.indices.reverse )
Но я глубоко не вникал, так по верхушкам посмотрел…
+1
Но ведь for используют не только для коллекций. Может мне нужно факториал посчитать или еще что-то… Какую-то хитрую сумму или еще что-то математическое. Не только же вектора гонять им
0
Ну в 98% случаев его используют, чтобы «вектора гонять» :-)
А для оставшихся случаев есть while.
А для оставшихся случаев есть while.
+1
В C# есть возможность написать
Возможно и в Котлине есть подобные механизмы.
foreach(var index in Enumerable.Range(3,456))
{
Console.WriteLine(index);
}
Возможно и в Котлине есть подобные механизмы.
0
согласен. автор молодец, наверняка кому-то будет более лицеприятен LINQ-подобный код, но я бы в реальных проектах не стал бы им грешить, все-таки очень различается уровень разработчиков; если писать на С++ умеют многие, то читать и понимать, а тем более модифицировать — единицы. лучше бы облегчить им задачу.
я даже бустом стараюсь пользоваться аккуратно, если нужен regex_match — пожалуйста, но если BOOST_FOREACH, то лучше вписать обычный for, пусть и не так компактен, зато и при беглом взгляде понятно что происходит.
я даже бустом стараюсь пользоваться аккуратно, если нужен regex_match — пожалуйста, но если BOOST_FOREACH, то лучше вписать обычный for, пусть и не так компактен, зато и при беглом взгляде понятно что происходит.
0
Полностью поддерживаю
0
а почему «LINQ to Entities»? Вы изобразили «LINQ to Objects». А если честно, то причём здесь LINQ вообще, если это просто функции высшего порядка? :)
+5
Вот только у вас получился не Linq2Entities, а Linq2Objects: у вас нет той вещи, которая, собственно, и делает LINQ настолько мощным — AST.
0
Ну да, это бы убило типизацию C++. Я старался её сохранить.
Название статьи я поправил, выше уже указали на ошибку.
Название статьи я поправил, выше уже указали на ошибку.
+2
Ага, и настолько же «протекшим», ибо каждый IQueryProvider разбирает синтаксическое дерево так, как ему заблагорассудится. Ну это так, к слову.
+2
Это правда. Но сам механизм, позволяющий получать и передавать выражения, чтобы выполнять их так, как нужно, был и остается очень полезен. Именно на нем строится дикое количество современного кода.
+1
С этим не спорю. Как абстракция, LINQ — fail, но как инструмент — просто офигенен.
Сам предпочитаю его любым другим способам записи (причем в «первозданном» виде — без ключевых слов, а в виде вызовов Extension-методов).
Сам предпочитаю его любым другим способам записи (причем в «первозданном» виде — без ключевых слов, а в виде вызовов Extension-методов).
+1
Я, кстати, тоже предпочитаю method chain. Причина проста — не все можно выразить через ключевые слова, а смесь обоих подходов в одном коде выглядит ужасно.
+1
Это да, но кроме этого еще и читабельность. Запись «from item in someCollection select ...» мне категорически не нравится. Если бы где-то фигурировало «from someCollection» было бы понятно, но «from item» — какого лешего.
+1
Потому что это do-нотация хаскеля, заделанная под что-то sql-подобное путем замены слов. А в хаскеле пишется именно в таком порядке, если не ошибаюсь, то будет
item < — someCollection; return…
item < — someCollection; return…
0
Это, конечно, все бесспорно круто… но… зачем? :) Признаюсь, мне всего два раза в жизни хотелось что-то подобное. В остальных случаях C++11 и STL (ну и в крайнем случае boost) прекрасно справлялись. Не часто требуется задача, в которой надо сразу применить кучу фильтров.
Вообще, основная проблема здесь в том, что в C++ нет функциональщины. Ему нужен map и filter. Ох, как их порою не хватает.
Вообще, основная проблема здесь в том, что в C++ нет функциональщины. Ему нужен map и filter. Ох, как их порою не хватает.
+4
map — std::for_each, fold — std::accumulate
PROFIT
PROFIT
+1
map — std::for_each
Неужто мне тебя учить? Ты же любишь Haskell, ты должен знать, что в контексте функционального программирования переменных нет — есть только константы. А значит,
std::for_each
не должен менять существующий контейнер, а должен возвращать новый. std::accumulate
Вообще не понятно к чему это ты приплел. Я за fold хоть что-то сказал? Я говорил за
filter
.+2
Неужто мне тебя учить? Ты же любишь Haskell, ты должен знать, что в контексте функционального программирования переменных нет — есть только константы. А значит, std::for_each не должен менять существующий контейнер, а должен возвращать новый.
Не, учить меня не надо пока. Если тебе хочется новый контейнер, то std::transform.
Вообще не понятно к чему это ты приплел. Я за fold хоть что-то сказал? Я говорил за filter.
Неужто мне тебя учить? )) Фильтр же — это свёртка списка.
template <typename Container, typename InputIterator, typename UnaryPredicate>
Container filter(InputIterator _from, InputIterator _to, UnaryPredicate _pred)
{
Container collection;
return std::accumulate(_from, _to, collection,
[_pred] (Container acc, const InputIterator::value_type & val) -> Container
{
if (_pred(val))
acc.insert(std::end(acc), val);
return acc;
});
}
//////////////////////////////
// usage
std::vector<int> vec = {0, 1, 2, 3};
std::vector<int> newVec = filter<decltype(newVec)>(std::begin(vec), std::end(vec),
[] (int n)
{
return n % 2 == 0;
});
Парочку оптимизаций сделать и всё. Ну или можно через итераторы опять же переписать. Юзать back_inserter какой-нить.
+5
Я тупанул собсно. Можно даже без возвращаемого типа обойтись. Всё равно он будет такой же, как и тип входной коллекции.
+2
Насколько я понял, посмотрев исходники автора, основная фича (кроме синтаксического сахара) в том, что никаких промежуточных контейнеров во время выполнения всего «linq»-выражения не создается, только конечный, во время выполнения toSomething.
where, select и т.п. формируют что-то типа АСТ
where, select и т.п. формируют что-то типа АСТ
+2
Увидев всё это я начал делать boolinq…
+3
Я тоже однажды сел писать то же самое, что написали Вы. Если что, pull-реквесты принимаются? )
+1
Если тебе хочется новый контейнер, то std::transform.
Да брось! Это же неудобно. Да и новый контейнер предварительно придется создавать, т.к. transform принимает OutputIterator, из-за чего нельзя будет делать несколько map'ов и filter'ов в одну строку. :)
Неужто мне тебя учить? )) Фильтр же — это свёртка списка.
Ты всерьез думаешь, что делать filter через std::acumulate — это нормально?)
Вопрос стоит в удобстве, а не в возможности. В противном случае, библиотеки подобного типа и не нужны были бы вовсе. :)
+1
Если вопрос для тебя стоит в удобстве, то лучше в С++ вообще функциональщину не тянуть. Проще заюзать Haskell, в котором всё это отлично реализовано. А если хочется изврата, то Boost.Phoenix спецом для тебя.
Ты сказал, что проблема в том, мол, не хватает якобы map и filter. Я ответил, что проблемы никакой нету, ибо map на самом деле имеется, только называется по-другому и выполнен в STL-style. А filter может и нету, но он легко реализуется через свёртку списка, если уж функциональщину брать за основу.
Ты сказал, что проблема в том, мол, не хватает якобы map и filter. Я ответил, что проблемы никакой нету, ибо map на самом деле имеется, только называется по-другому и выполнен в STL-style. А filter может и нету, но он легко реализуется через свёртку списка, если уж функциональщину брать за основу.
0
Фильтр же — это свёртка списка.
Фильтр — это фильтр. То, что в где-то он определен через фолд не значит, что так же нужно делать в С++. В С++ так делать зло и если охота, нужно подумать, а стоило ли вообще С++ выбирать.
std::remove_copy_if, который вы тут навелосипедили через std::accumulate, нужно реализовывать через цикл, будет смотреться намного чище.
+2
ikalnitsky попросил функциональщины — я дал ему функциональщины. Не нужно мне говорить, что я и где навелосипедил. Или Вы думаете, что у меня в production такой код? ))
0
Признаюсь, мне всего два раза в жизни хотелось что-то подобное.
И один из них, дай-ка угадаю, был в этом треде? :)
+1
Я тут заметил, что используется только
#pragma once
, а он, к сожалению, не является стандартным. Поэтому, если хочется оставить преимущества последнего и не пренебрегать стандартом С++, лучше воспользоваться следующей конструкцией:#pragma once
#ifndef BOOLINQ_H_
#define BOOLINQ_H_
// paste boolinq code here
#endif // BOOLINQ_H_
+1
Все популярные компиляторы поддерживают once
+1
Все популярные компиляторы поддерживают RVO и прочие распространенные оптимизации. Вы пишите код, который сразу рассчитывает на оптимизацию и некорректен с выключенной оптимизацией? :)
Я люблю писать по максимуму в рамках Стандарта C++.
К тому же, даже разработчики MS в своей реализации STL делают подобный финт: include guard + pragma once.
Я люблю писать по максимуму в рамках Стандарта C++.
К тому же, даже разработчики MS в своей реализации STL делают подобный финт: include guard + pragma once.
0
Не знаю, к чему относится первая часть комментария. Но, отвечу, — нет.
По поводу MS разработчиков — если они так делают, вовсе не означает, что так нужно делать всем. Не вижу причин не использовать once, если нет желания поддержки экзотических компиляторов
По поводу MS разработчиков — если они так делают, вовсе не означает, что так нужно делать всем. Не вижу причин не использовать once, если нет желания поддержки экзотических компиляторов
0
Зачем тогда оставлять pragma? Насколько это действительно оправдано. Какие компиляторы этого не поддерживают? В библиотеке используется C++11. Я думаю это предъявляет более жесткие требования к используемым компиляторам…
+1
pragma once
якобы быстрее. Как я указал в комментарии выше — разработчики MS в своей STL юзают и pragma и include guard.0
Типа да. Для инклуд-гардов препроцессору по ходу всё равно нужно прочитать весь файл.
0
Не, ну если в STL так кодят, то это уже совсем другой разговор) Тогда это можно просто считать хорошим стилем. Я думал уже все развивающиеся компиляторы тянут эту возможность…
+1
Для двух полей можно так:
struct S
{
int f1,f2;
};
vector<S> v;
int sum=std::accumulate(v.begin(),v.end(),S(),[](S x, S y){S t; t.f1=x.f1+y.f1; return t;}).f1;
+1
И это речь идёт о подсчёте суммы… Согласитесь, жесть. А если их надо перемножить или дисперсию посчитать?
+2
Согласен, но я не знаком с концпецией LINQ и с вашей реализацией, и для меня код, предложенный вами тоже не совсем очевиден. Для меня цикл тут видится наиболее логичным решением. Возможно вам стоило выбрать пример, на котором преимущества были бы более очевидны.
0
int sum = 0;
for (Candle candle : vector)
sum += candle.ClosePrice;
".., но Visual Studio 2010 увы эту фичу не поддерживает:" — бздёшь и провокация :-)
for each(auto candle in vector)
sum += candle.ClosePrice;
for each(Candle candle in vector)
sum += candle.ClosePrice;
for each(Candle& candle in vector)
sum += candle.ClosePrice;
+2
Офигеть!)) И вправду: msdn.microsoft.com/en-us/library/ms177202(v=vs.80).aspx
+1
Интересно, в каком стандарте это описано.
+3
Вы мне открыли глаза о_О
Хотя именно с++11 фичу она не поддерживает, for each в студии это CLR фича, работающая в нейтив коде по непонятным причинам…
Хотя именно с++11 фичу она не поддерживает, for each в студии это CLR фича, работающая в нейтив коде по непонятным причинам…
+3
Microsoft не раз говорил что они не рекомендуют ей пользоваться, она глючная и не поддерживается в других компиляторов.
Лучше используйте BOOST_FOREACH или подождите Visual Studio 11, там будет поддерживаться range-based for из C++11:
Лучше используйте BOOST_FOREACH или подождите Visual Studio 11, там будет поддерживаться range-based for из C++11:
std::vector<int> numbers;
for(int i : numbers){
// blah blah
}
+3
Написано хоршо, я бы отправил в буст после допиливания :)
Вы могли бы в статью добавить освещение того, как оно работает изнутри, чтобы можно было понять насколько оно медленнее выполняется по сравнению с обычным кодом? Я немножко смотрел исходники, вроде бы там все достаточно хорошо в этом плане, но все-таки хотелось бы увидеть комментарии в удобном для восприятия формате, хотя бы основные идеи — в сжатой форме.
Вы могли бы в статью добавить освещение того, как оно работает изнутри, чтобы можно было понять насколько оно медленнее выполняется по сравнению с обычным кодом? Я немножко смотрел исходники, вроде бы там все достаточно хорошо в этом плане, но все-таки хотелось бы увидеть комментарии в удобном для восприятия формате, хотя бы основные идеи — в сжатой форме.
+3
Думаю дописать принципы работы. Там есть несколько особенностей интересных.
В бусте что-то похожее есть, но синтаксис конечно, пугает и мало реализовано функций…
В бусте что-то похожее есть, но синтаксис конечно, пугает и мало реализовано функций…
+2
Особенно хочется понять как работает сортировка…
+1
Сначала код был очень простой. Сложным он стал когда надо было первые значения front() и back() тоже отложенно вычислять, чтобы была возможность работать с Input Iterator
+1
Допишите, допишите, все дописывайте. Очень крутая штука. (А меня особенно отложенность интересует.)
+2
Написал основные принципы — вышла целая статья...
+3
Крайне приятная реализация, очень интересна портируемость на GCC. Как я понимаю, пока все проверялось в рамках Visual C++.
+3
Проверял на GCC. Для меня это приоритетное направление. Даже держал PRО-файл для QtCreator — боюсь он уже устарел порядком… Надо бы обновить. Часто приходится писать в креаторе…
+3
Думаю есть смысл добавить это в описание на гугло-коде, а то я уже почти сел собирать/проверять (наверное и не я один)
0
Скажем так, последний раз проверка на совместимость с GCC была ой как давно, но я обязательно попробую на днях — и в случае чего — поправлю несовместимости…
+2
То есть в C++ тоже ещё нет более-менее полноценной реализации LINQ? Что ж, значит, PHP — не единственный язык, в котором все библиотеки а-ля LINQ кошмарны. :) Всё хочу на досуге версией для похапэ заняться, потому что без слёз на текущие библиоткеи смотреть нельзя.
Если реализовать большинство методов LINQ, то это будет офигительно круто. Причём хорошо, что учитывается специфика языка (тяга к bytes и bits в C++, например). Очень показательна в этом плане аналогичная библиотека для JavaScript (там преобразование между объектами и массивами и прочие странности, активно использующиеся в языке), на совесть сделана.
После появления лямбд с плюсах не может не появиться LINQ to objects. Это мощнейший инструмент, после использования которого в шарпе не представляешь, как можно жить без него. :)
Успехов!
Если реализовать большинство методов LINQ, то это будет офигительно круто. Причём хорошо, что учитывается специфика языка (тяга к bytes и bits в C++, например). Очень показательна в этом плане аналогичная библиотека для JavaScript (там преобразование между объектами и массивами и прочие странности, активно использующиеся в языке), на совесть сделана.
После появления лямбд с плюсах не может не появиться LINQ to objects. Это мощнейший инструмент, после использования которого в шарпе не представляешь, как можно жить без него. :)
Успехов!
+2
Для PHP есть Underscore.PHP, вполне вменяемая замена ужасным функциям стандартной библиотеки. Не LINQ безусловно, но рекомендую взлянуть прежде чем начинать писать код.
0
Посмотрел, ленивых вычислений нет. Без них это уже совсем не LINQ. Уже не получится написать:
Как замена стандартным функциям тоже не подходит, реализовано слишком малое количество функций.
from(naturalNumbers).select(i % 2 == 0) // нет бесконечности
from(array).where(i > 0).any() // будет полный проход
Как замена стандартным функциям тоже не подходит, реализовано слишком малое количество функций.
0
а можно пример использования groupBy? как привести его результат к определенному контейнеру?
+1
Допишу сегодня
0
Вот ссылка на тесты groupBy
0
Прикольно, пожалуй запомню :) Кстати, а с clang'ом оно как? Я так понимаю ждать 3.1?
+2
Я вот подумал, а что если переоформить код в соответствии с Qt coding conventions и запульнуть на qt-project? Штука весьма удобная и очевидная.
+1
Крутая штука, которая просто обязана была появиться в C++11. Есть какой-то опыт использования boolinq в продакшене?
+3
На GoingNative говорили, что стандартная библиотека нуждается в новом функционале от сторонних разработчиков. Я бы ОЧЕНЬ хотел видеть что-то подобное в стандартной библиотеке. Вы бы не могли отправить ее комитету? Это будет стоить Вам многих эфортов и даст Вам мало профита, но все же я надеюсь.
+1
еще такой вопрос, насколько эффективно работать с большими векторами? в коде проскакивали push_back… выделяется ли память под размер, например, результата отработанного where, или же могут происходить перевыделения памяти во время его отработки? если не выделяется, то есть ли пути это реализовать?
ну, еще по мелочи, возможность достучаться до индекса элемента тоже оказалась бы полезной)
ну, еще по мелочи, возможность достучаться до индекса элемента тоже оказалась бы полезной)
+1
Вот что делает с людьми возвращение к C++ из C#. К удобному привыкаешь быстро.
0
Sign up to leave a comment.
LINQ to Objects на языке C++