Есть ли любые преимущества std::for_each
for
цикл? Мне, std::for_each
только, кажется, препятствует удобочитаемости кода. Почему делают затем, некоторые стандарты кодирования рекомендуют его использование?
Приятная вещь с C ++ 11 (ранее называемая C ++ 0x), состоит в том, что эта утомительная дискуссия будет урегулирована.
Я имею в виду, никто в своем правом уме, кто хочет повторять целую коллекцию, все равно будет использовать эту
for(auto it = collection.begin(); it != collection.end() ; ++it)
{
foo(*it);
}
или это
for_each(collection.begin(), collection.end(), [](Element& e)
{
foo(e);
});
, когда на основе диапазона
для
Синтаксис доступен:
for(Element& e : collection)
{
foo(e);
}
Этот вид синтаксиса был доступен в Java и C # в течение некоторого времени, а на самом деле есть больше Foreach
петли, чем классические для петель
В каждом недавнем коде Java или C # я видел.
Вот несколько причин:
, кажется, мешает читабельности только потому, что вы не используете это и / или не используете правильные инструменты вокруг него, чтобы сделать это действительно легко. (См. Boost :: Range и Boost :: Bind / Boost :: лямбда для помощников. Многие из них будут переходить на C ++ 0x и сделать For_each и связанные функциями более полезными.)
Это позволяет писать алгоритм на Вершина for_each, которая работает с любым итератором.
Это уменьшает вероятность глупых теховых ошибок.
Это также открывает ваш разум до остальных алгоритмов STL-алгоритмов, таких как find_if
, ,
, ,
, и т. Д., И они не будут выглядеть так странно больше. Это может быть огромная победа.
Обновление 1:
Самое главное, это помогает вам выйти за пределы for_each
vs. for-poots, подобные это все, что есть, и посмотрите на другие STL-Alogs, например, найти / сортировать / раздел / copy_replace_if, Параллелл выполнение .. или что угодно.
Многие обработки могут быть написаны очень кратко с использованием «остальных» братьев и сестер For_each, но если все, что вы делаете, это написать на петлю с различной внутренней логикой, вы никогда не узнаете, как их использовать, и Вы закончите изобретать колесо снова и снова.
и (в ближайшее время, доступный диапазон for_each):
for_each(monsters, boost::mem_fn(&Monster::think));
или с лямбдами C ++ x11:
for_each(monsters, [](Monster& m) { m.think(); });
IMO более читабелее, чем:
for(Monsters::iterator i = monsters.begin(); i != monsters.end(); ++i) {
i->think();
}
также это (или с лямбдасом, видят других ):
for_each(bananas, boost::bind(&Monkey::eat, my_monkey, _1));
Более кратко, чем:
for(Bananas::iterator i = bananas.begin(); i != bananas.end(); ++i) {
my_monkey->eat(*i);
}
, особенно если у вас есть несколько функций, чтобы позвонить в порядок ... Но, возможно, это только я. ;)
Обновление 2 : Я написал свои собственные одноищеные обертки STL-ALGOS, которые работают с диапазонами вместо пары итераторов. Boost :: Range_ex, однажды выпущенный, будет включать, что и, может быть, это будет там в C ++ 0x тоже?
Помимо читаемости и производительности, Одним из аспектов, обычно упускающих из виду. Существует множество способов реализации петли для (или во время) петли над итераторами, от:
for (C::iterator iter = c.begin(); iter != c.end(); iter++) {
do_something(*iter);
}
к:
C::iterator iter = c.begin();
C::iterator end = c.end();
while (iter != end) {
do_something(*iter);
++iter;
}
со многими примерами между различными уровнями эффективности и потенциала ошибки.
Использование for_each, однако, обеспечивает согласованность консистенцией путем абстрагирования петли:
for_each(c.begin(), c.end(), do_something);
Единственное, что вы должны беспокоиться сейчас, это: реализуете ли вы тело цикла как функцию, функцию функции или лямбда, используя повышение или C + + 0x Особенности? Лично я бы предпочел беспокоиться об этом, чем о том, как реализовать или прочитать случайную заводскую петлю.
Вы можете иметь, чтобы итератор был вызовом функции, которая выполняется на каждой итерации через петлю.
Смотрите здесь: http://www.cplusplus.com/Reeference/Algorithm/for_cheach/
его очень субъективно, некоторые скажут, что использование для _ каждый
сделает код более читаемым, так как он позволяет обрабатывать разные коллекции одними и теми же условностями.
для _ каждый
itslef реализован как цикл
template<class InputIterator, class Function>
Function for_each(InputIterator first, InputIterator last, Function f)
{
for ( ; first!=last; ++first ) f(*first);
return f;
}
, чтобы вы могли выбрать, что правильно для вас.
-121--670207-Feature : Bash, Korn shell (ksh93) и Z shell разрешают подстрочные массивы с переменными со знаком доллара или без него:
array[index]=$value
array[$index]=$value
Это, со знаком доллара, приведет к ожидаемому значению 10000:
unset array
for i in {1..10000}
do
((array[$RANDOM%6+1]++))
done
unset total
for count in ${array[@]}
do
((total += count))
done
echo $total
Странность : Если удалить знак доллара из
Аналогично, это приводит к 3 вместо 2:
a=1; ((r[a++]++)); echo $a
И вы не можете использовать знак доллара там, потому что это назначение (a на lhs), хотя вы могли бы сделать это, если бы вы использовали косвенность, но двойная оценка все еще происходит.
Причина : С помощью знака доллара расширение переменной выполняется до арифметической оценки, поэтому выполняется только один раз. Без знака доллара он выполняется дважды, один раз, чтобы вычислить индекс для поиска и еще раз, чтобы вычислить индекс для назначения (так, фактически, назначение на одном шаге цикла может выглядеть как массив [4] = $ массив [6] + 1
, который полностью скремблирует массив).
для
- цикл, который может выполнять итерацию каждого элемента или каждого третьего и т.д. , для _ каждый
предназначен только для итерации каждого элемента. Из его названия ясно. Так что более ясно, что вы собираетесь делать в своем коде.
for_each
более общего. Вы можете использовать его для итерации в течение любого типа контейнера (путем прохождения в итераторах начала / конечного). Вы можете потенциально обменять контейнеры под функцией, которая использует for_each
без необходимости обновлять код итерации. Вам необходимо учитывать, что в мире есть другие контейнеры, чем STD :: Vector
и простые старые старые C Armays, чтобы увидеть преимущества for_each
.
Основной недостаток for_each
заключается в том, что он принимает функтор, поэтому синтаксис неуклюжем. Это фиксируется в C ++ 0x с введением лямбдаса:
std::vector<int> container;
...
std::for_each(container.begin(), container.end(), [](int& i){
i+= 10;
});
Это не будет выглядеть странно для вас через 3 года.
В основном вам придется итерацию по всей коллекции . Поэтому я предлагаю вам написать свой собственный вариант for_each (), принимая только 2 параметра. Это позволит вам переписать Пример Терри Махаффи как:
for_each(container, [](int& i) {
i += 10;
});
Я думаю, что это действительно более читаемое, чем для цикла. Однако это требует расширений компилятора C ++ 0x.
Вы в основном правильно: большую часть времени STD :: for_each
- это чистый убыток. Я бы пошел так далеко, что и сравнить for_each
- GOTO
. GOTO
обеспечивает наиболее универсальный возможный контроль потока - вы можете использовать его для реализации практически любой другой элемент управления, которую вы можете себе представить. Это очень универсальность, однако, означает, что видя Goto
в изоляции в изоляции ничего НЕТ о том, что он предназначен для этой ситуации. В результате почти никто не имеет правильного разума GOTO
, кроме как в последнем курорте.
Среди стандартных алгоритмов for_each
намного одинаково - его можно использовать для реализации практически все, что означает, что видя for_each
, как это ничего о том, что это используется для в этой ситуации. К сожалению, отношение людей к for_each
о том, где их отношение к было в
(сказать) 1970 или около того - а мало кто Он должен использоваться только в качестве последнего курорта, но многие по-прежнему считают его основным алгоритмом, и редко, если когда-либо использовать любой другой. Подавляющее большинство того времени, даже быстрый взгляд раскроет, что одна из альтернатив резко распространена.
Просто например, я уверен, что я потерял путь, сколько раз я видел, как люди пишут код, чтобы распечатать содержимое коллекции, используя for_each
. Основываясь на постах, которые я видел, это может быть одно наиболее распространенное использование for_each
. Они в конечном итоге с чем-то вроде:
class XXX {
// ...
public:
std::ostream &print(std::ostream &os) { return os << "my data\n"; }
};
и их пост спрашивает о том, какая комбинация Bind1st
, mem_fun
и т. Д. И т. Д. Они должны сделать что-то вроде:
std::vector<XXX> coll;
std::for_each(coll.begin(), coll.end(), XXX::print);
работа Распечатать элементы COL
. Если бы он действительно работал точно так, как я написал там, это было бы посредстве, но это не так - и к тому времени, когда вы получили его на работу, трудно найти эти несколько битов кода, связанные с тем, что происходит среди штук, которые держат его вместе.
К счастью, есть гораздо лучший способ. Добавьте нормальную перегрузку в потоке в Inserter для XXX:
std::ostream &operator<<(std::ostream *os, XXX const &x) {
return x.print(os);
}
и используйте std :: copy
:
std::copy(coll.begin(), coll.end(), std::ostream_iterator<XXX>(std::cout, "\n"));
, который делает работу - и практически не занимает работу, чтобы выяснить, что он печатает содержимое Колл
на STD :: Cout
.
Структура for_each
предназначена для скрытия итераторов (детализация того, как реализован петля) из кода пользователя и определяет четкую семантику на операции: каждый элемент будет именно один раз.
Проблема с читаемостью в текущем стандарте заключается в том, что она требует функтора в качестве последнего аргумента вместо блока кода, поэтому во многих случаях вы должны записать конкретный тип функтора для него. Это превращается в менее читаемый код, поскольку объекты функтора не могут быть определены на месте (локальные классы, определенные в функции, нельзя использовать в качестве аргументов шаблона), и реализация петли должна быть перемещена от фактической петли.
struct myfunctor {
void operator()( int arg1 ) { code }
};
void apply( std::vector<int> const & v ) {
// code
std::for_each( v.begin(), v.end(), myfunctor() );
// more code
}
Обратите внимание, что если вы хотите выполнить определенную работу на каждом объекте, вы можете использовать std :: mem_fn
, или Boost :: Bind
( std :: bind
В следующем стандарте), или BOOST :: лямбда
(лямбдас в следующем стандарте), чтобы сделать его проще:
void function( int value );
void apply( std::vector<X> const & v ) {
// code
std::for_each( v.begin(), v.end(), boost::bind( function, _1 ) );
// code
}
, который не менее читабелен и более компактным, чем ручная версия, если вы У вас есть функция / метод, чтобы позвонить на место. Реализация может обеспечить другие реализации LOOP for_each
(подумайте о параллельной обработке).
Предстоящий стандарт позаботится о некоторых недостатках по-разному, он позволит локально определенным классам в качестве аргументов к шаблонам:
void apply( std::vector<int> const & v ) {
// code
struct myfunctor {
void operator()( int ) { code }
};
std::for_each( v.begin(), v.end(), myfunctor() );
// code
}
Улучшение местности кода: когда вы просматриваете, вы видите, что он делает прямо там. На самом деле, вам даже не нужно использовать синтаксис класса, чтобы определить функтор, но использовать лямбду прямо там:
void apply( std::vector<int> const & v ) {
// code
std::for_each( v.begin(), v.end(),
[]( int ) { // code } );
// code
}
, даже если для случая for_each
будет конкретный Конструкция, которая сделает его более естественным:
void apply( std::vector<int> const & v ) {
// code
for ( int i : v ) {
// code
}
// code
}
Я склонен смешивать постройку for_each
с ручными рулевыми петлями. Когда только вызов к существующей функции или методу - это то, что мне нужно ( for_each (v.begin (), v.end (), Boost :: Bind (& Тип :: Обновление, _1))
) I Перейти к конструкции for_each
, которая убирает из кода много вещей итераторной пластины. Когда мне нужно что-то более сложное, и я не могу реализовать функтор всего пару строк выше фактического использования, я катаю свой собственный цикл (сохраняет работу на месте). В не критически важных участках кода я мог бы пойти с Boost_foreach (сотрудник у меня в него в него)
Необходимо использовать viewWillAppean:
для установки состояния представления до его появления каждый раз, даже если представление уже отображалось ранее. Любое невидимое представление в UITabViewController или UINavigationViewController может быть выгружено в любое время, поэтому нельзя рассчитывать на то, что невидимое представление сохранит свое состояние.
Или для более точного управления реализуйте loadView
, viewDidLoad
и viewDidUnload
.
Если вы поддерживаете собственную иерархию контроллеров ракурсов, вам придется вручную пересылать сообщения viewWillAppean, viewWillansapear и т.д. вашим контроллерам подчиненных ракурсов.
-121--3357904- Посмотрите на CommonCrypto/CommonDigest.h
.
Функция CC _ MD5 (const void * data, CC_LONG len, неподписанный символ * md);
вычисляет хеш MD5.
@implementation NSData (MD5)
-(NSString*)md5
{
unsigned char digest[CC_MD5_DIGEST_LENGTH];
CC_MD5([self bytes], [self length], digest);
NSString* s = [NSString stringWithFormat: @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
digest[0], digest[1],
digest[2], digest[3],
digest[4], digest[5],
digest[6], digest[7],
digest[8], digest[9],
digest[10], digest[11],
digest[12], digest[13],
digest[14], digest[15]];
return s;
}
@end
При развертывании файлов на сервере можно использовать OpenSSL для вычисления хэшей. Команда openssl md5 filename
вычисляет хэш MD5 для файла. Это может быть интегрировано в сценарий.
После загрузки файла приложение вычисляет хэш загруженного файла и сравнивает его с хэшем, сохраненным на сервере.
Очевидно, что если вы хотите обеспечить целостность файла plist, этот plist не может содержать свой собственный хэш.
-121--4435337- его очень субъективно, некоторые скажут, что использование для _ каждый
сделает код более читаемым, так как позволяет обрабатывать разные коллекции одними и теми же условностями.
для _ каждый
itslef реализован как цикл
template<class InputIterator, class Function>
Function for_each(InputIterator first, InputIterator last, Function f)
{
for ( ; first!=last; ++first ) f(*first);
return f;
}
, чтобы вы могли выбрать, что правильно для вас.
Функция : bash, оболочка korn (ksh93) и оболочка z каждая из которых разрешают массивы подписчиков с переменными с или без знака доллара:
array[index]=$value
array[$index]=$value
это, с знаком доллара, будет производить ожидаемое значение 10000 :
unset array
for i in {1..10000}
do
((array[$RANDOM%6+1]++))
done
unset total
for count in ${array[@]}
do
((total += count))
done
echo $total
Счетность : Если вы удалите знак доллара из случайных, общая сумма будет варьироваться случайным образом, даже более 10000.
аналогично, это производит 3 вместо 2:
a=1; ((r[a++]++)); echo $a
, и вы можете t Используйте знак доллара, потому что это назначение (A - это на LHS), хотя вы могли бы сделать это, если бы вы использовали косвенное управление, но двойная оценка по-прежнему происходит.
Причина : с знаком доллара, переменная расширение выполняется до арифметической оценки, поэтому только один раз выполняется. Без знака доллара он выполняется дважды, один раз, один раз, когда вы рассчитываете индекс для поиска и снова для расчета индекса для назначения (так, по существу, присвоение на один шаг в цикле может выглядеть как Array [4] = $ Array [6] + 1
, которые полностью окрашивают массив).
Как и многие из функций алгоритма, первоначальная реакция заключается в том, чтобы подумать, что это более нечитаемо, чтобы использовать Foreach, чем цикла. Это была тема многих пламенных войн.
Как только вы привыкнете к IDIOM, вы можете найти его полезным. Одним из очевидных преимуществ является то, что он заставляет кодер отделить внутреннее содержимое цикла из фактической функциональности итерации. (Хорошо, я думаю, что это преимущество. Другие говорят, что вы просто раздуваете код без реального Benifit.
Одним из других преимуществ является то, что когда я вижу FOREACH, I знаю , что либо каждый элемент будет обработан, либо исключение, будет брошено.
A для цикла позволяет нескольким вариантам для завершения цикла. Вы можете позволить циклу запустить свой полный курс, или вы можете использовать ключевое слово Break , чтобы явно выпрыгнуть из петли или использовать ключевое слово return , чтобы выйти из всей функции Mid-Coot Отказ Напротив, Foreach не позволяет этим вариантам, и это делает его более читаемым. Вы можете просто взглянуть на имя функции, и вы знаете полную природу итерации.
Вот пример запутанного для цикла :
for(std::vector<widget>::iterator i = v.begin(); i != v.end(); ++i)
{
/////////////////////////////////////////////////////////////////////
// Imagine a page of code here by programmers who don't refactor
///////////////////////////////////////////////////////////////////////
if(widget->Cost < calculatedAmountSofar)
{
break;
}
////////////////////////////////////////////////////////////////////////
// And then some more code added by a stressed out juniour developer
// *#&$*)#$&#(#)$#(*$&#(&*^$#(*$#)($*#(&$^#($*&#)$(#&*$&#*$#*)$(#*
/////////////////////////////////////////////////////////////////////////
for(std::vector<widgetPart>::iterator ip = widget.GetParts().begin(); ip != widget.GetParts().end(); ++ip)
{
if(ip->IsBroken())
{
return false;
}
}
}
Преимущество функционала написания для фишек более читаемого, может не отображаться, когда для (...)
и for_each (...
) Отказ
Если вы используете все алгоритмы в Functional.h вместо использования для петлей, код получает гораздо более читаемый;
iterator longest_tree = std::max_element(forest.begin(), forest.end(), ...);
iterator first_leaf_tree = std::find_if(forest.begin(), forest.end(), ...);
std::transform(forest.begin(), forest.end(), firewood.begin(), ...);
std::for_each(forest.begin(), forest.end(), make_plywood);
- это много более читаемого, чем;
Forest::iterator longest_tree = it.begin();
for (Forest::const_iterator it = forest.begin(); it != forest.end(); ++it{
if (*it > *longest_tree) {
longest_tree = it;
}
}
Forest::iterator leaf_tree = it.begin();
for (Forest::const_iterator it = forest.begin(); it != forest.end(); ++it{
if (it->type() == LEAF_TREE) {
leaf_tree = it;
break;
}
}
for (Forest::const_iterator it = forest.begin(), jt = firewood.begin();
it != forest.end();
it++, jt++) {
*jt = boost::transformtowood(*it);
}
for (Forest::const_iterator it = forest.begin(); it != forest.end(); ++it{
std::makeplywood(*it);
}
, и это то, что Я думаю, что так приятно, обобщена для петлей к одной линии функции =)
Я нахожу for_each, чтобы быть плохим для читабельности. Концепция хорошая, но C ++ очень трудно написать читаемость, по крайней мере, для меня. С ++ 0x Lamda выражения поможет. Мне очень нравится идея ламдаса. Однако на первый взгляд, я думаю, что синтаксис очень уродливый, и я не на 100% уверен, что я когда-либо привыкнут к этому. Может быть, через 5 лет я привык к этому и не даю ей вторую мысль, но, возможно, нет. Время скажет :)
Я предпочитаю использовать
vector<thing>::iterator istart = container.begin();
vector<thing>::iterator iend = container.end();
for(vector<thing>::iterator i = istart; i != iend; ++i) {
// Do stuff
}
, я нахожу явную цикл более понятнее для чтения и откровенности с использованием именованных переменных для начальных и конечных итераторов уменьшает беспорядок в цикле.
Конечно, случаи варьируются, это только то, что я обычно нахожу лучше всего.
Раньше я не любил std :: for_each
и думал, что без лямбда это было сделано совершенно неправильно. Однако я изменил свой разум некоторое время назад, и теперь я на самом деле люблю это. И я думаю, что он даже улучшает читаемость и облегчает проверку вашего кода в виде TDD.
Алгоритм ATD :: for_each
можно прочитать как сделать что-то со всеми элементами в диапазоне , что может улучшить читаемость. Скажите, что действие, которое вы хотите выполнить, - это 20 строк, и функция, в которой выполняется действие, также длится около 20 строк. Это сделало бы функцию 40 строк длиной с обычным для петли, и только около 20 с STD :: for_each
, что, вероятно, легче понять.
Функторы для STD :: for_each
, скорее всего, будут более универсальными, и, таким образом, многоразовые, например:
struct DeleteElement
{
template <typename T>
void operator()(const T *ptr)
{
delete ptr;
}
};
и в коде, который вы имеете в виду только один вкладыш :: for_each (v.begin (), v.end (), delecteelement ())
, который немного лучше IMO, чем явная петля.
Все эти функторы обычно легче получить в тесты подразделения, чем явную для петли в середине длительной функции, и только это уже большая победа для меня.
std :: for_each
также, как правило, более надежным, так как вы менее склонны допустить ошибку с диапазоном.
И, наконец, компилятор может производить немного лучшего кода для std :: for_each
, чем для определенных типов ручной работы для цикла, как он (for_each) всегда выглядит одинаково для Компилятор, а писатели компилятора могут поставить все свои знания, чтобы сделать это так же хорошо, как они могут.
То же самое относится к другим алгоритмам СТД, как FACE_IF
, преобразование
и т. Д.
Лично, в любое время, когда мне нужно будет выйти из моего пути к использованию STD :: for_each
(написать специальные функторы / сложные :: лямбда
), Я нахожу BOOST_FOREACK
и диапазона C ++ 0x для более четко:
BOOST_FOREACH(Monster* m, monsters) {
if (m->has_plan())
m->act();
}
против
std::for_each(monsters.begin(), monsters.end(),
if_then(bind(&Monster::has_plan, _1),
bind(&Monster::act, _1)));