2015-06-26 1 views
23

Оба могут использоваться для применения функции к ряду элементов.В чем разница между std :: transform и std :: for_each?

На высоком уровне:

  • std::for_each игнорирует возвращаемое значение функции, и гарантирует порядок исполнения.
  • std::transform присваивает возвращаемое значение итератору, а не гарантирует порядок исполнения.

Когда вы предпочитаете использовать один против другого? Есть ли какие-то тонкие оговорки?

+7

'transform' имеет выходной диапазон,' for_each' нет. – user657267

+4

Множество '' состоит из функций, которые очень похожи, просто имеют * незначительную * разницу. – o11c

+0

@ o11c это именно то, что я ищу. Должна быть веская причина для небольшой разницы. – bendervader

ответ

27

std::transform такая же, как map. Идея состоит в том, чтобы применить функцию к каждому элементу между двумя итераторами и получить другой контейнер, состоящий из элементов, возникающих в результате применения такой функции. Вы можете использовать его для, например, проецирования элемента данных объекта в новый контейнер. В последующем std::transform используется для преобразования контейнера std::string с в контейнер std::size_t с.

std::vector<std::string> names = {"hi", "test", "foo"}; 
std::vector<std::size_t> name_sizes; 

std::transform(names.begin(), names.end(), std::back_inserter(name_sizes), [](const std::string& name) { return name.size();}); 

С другой стороны, вы выполняете std::for_each для единственных побочных эффектов. Другими словами, std::for_each очень похож на петлю for на основе простого диапазона.

Перейти к примеру строки:

std::for_each(name_sizes.begin(), name_sizes.end(), [](std::size_t name_size) { 
    std::cout << name_size << std::endl; 
}); 

В самом деле, начиная с 11 С ++ то же самое может быть достигнуто с использованием обозначений terser диапазона на основе for петли:

for (std::size_t name_size: name_sizes) { 
    std::cout << name_size << std::endl; 
} 
+2

, чтобы расширить это, в C++ 14 мы можем удалить тип из диапазона, основанного на цикле, чтобы теперь быть: for (name_size: name_sizes) {...} –

+0

приятно! в прошлый раз я проверил это было просто предложение. Обычно я использую 'for (auto && x: xs)', но этот уровень абстракции не был действительно важен для объяснения. –

+0

@TrevorHickey Я быстро проверил еще раз, и я не могу найти никаких новостей, кроме оригинального предложения N3853. Вы в этом уверены? –

0

Реальный пример использования зЬй :: tranform, когда вы хотите, чтобы преобразовать строку в верхний регистр, вы можете написать код, как это:

std::transform(s.begin(), s.end(), std::back_inserter(out), ::toupper); 

, если вы будете пытаться достичь то же самое с станд :: for_each как:

std::for_each(s.begin(), s.end(), ::toupper); 

это обыкновение преобразовать его в верхний регистр строки

12

Ваш высокий Обзор уровня

  • std::for_each игнорирует возвращаемое значение функции и гарантирует порядок выполнения.
  • std::transform присваивает возвращаемое значение итератору и не гарантирует порядок выполнения.

довольно много покрывает его.

Другой способ взглянуть на него (предпочитает один над другим);

  • Получают ли результаты (возвращаемое значение) значение операции?
  • Является ли операция для каждого элемента методом члена без возвращаемого значения?
  • Есть ли два диапазона входных сигналов?

Еще одна вещь, чтобы иметь в виду (тонкое предостережение) является изменение требований операций std::transform до и после C++ 11 (от en.cppreference.com);

  • Перед C++ 11, они должны были «не имеют каких-либо побочных эффектов»,
  • После C++ 11, это изменено на «не должно привести к недействительности итераторов, в том числе конечных итераторы, или изменить любые элементы используемых диапазонов »

В основном это должно было обеспечить неопределенный порядок выполнения.

Когда я использую один над другим?

Если я хочу манипулировать каждым элементом в диапазоне, то использую for_each. Если мне нужно вычислить что-то из каждого элемента, то я бы использовал transform. При использовании for_each и transform, я обычно соединяю их с лямбдой.

То есть, я нашел мое текущее использование традиционного for_each быть несколько уменьшилась с момента появления диапазона на основе for петель и лямбды в C++ 11 (for (element : range)). Я считаю, что его синтаксис и реализация очень естественны (но ваш пробег здесь будет отличаться) и более интуитивно понятный для некоторых случаев использования.

+0

Вы могли бы немного рассказать о том, почему «std :: transform ... не гарантирует порядок выполнения»? – athos

+1

Это в отличие от алгоритма 'for_each', требуется применить функцию к каждому элементу по порядку. Требования операций 'transform', чтобы не иметь каких-либо побочных эффектов, означает, что порядок выполнения не будет наблюдаемым, поэтому не гарантируется. Все сказанное: все реализации, которые я рассматривал, применяют в ожидаемом порядке, в первую очередь. – Niall

8

Несмотря на то, что на вопрос был дан ответ, я считаю, что этот пример прояснит разницу.

for_each относится к немодулируемым операциям STL, что означает, что эти операции не меняют элементы коллекции или самой коллекции. Следовательно, значение , возвращаемое for_each, всегда игнорируется и не присваивается элементу коллекции. Тем не менее, все еще можно изменять элементы коллекции, например, когда элемент передается функции f с использованием ссылки. Следует избегать такого поведения, поскольку оно не согласуется с принципами STL.

В противоположности этому, transform функции принадлежит операции модификации STL и применяет данный предикат (unary_op или binary_op) к элементам коллекции или коллекциям и результатам магазина в другой коллекции.

#include <vector> 
#include <iostream> 
#include <algorithm> 
#include <functional> 
using namespace std; 

void printer(int i) { 
     cout << i << ", "; 
} 
int main() { 
    int mynumbers[] = { 1, 2, 3, 4 }; 
    vector<int> v(mynumbers, mynumbers + 4); 

    for_each(v.begin(), v.end(), negate<int>());//no effect as returned value of UnaryFunction negate() is ignored. 
    for_each(v.begin(), v.end(), printer);  //guarantees order 

    cout << endl; 

    transform(v.begin(), v.end(), v.begin(), negate<int>());//negates elements correctly 
    for_each(v.begin(), v.end(), printer); 
    return 0; 
} 

, который будет печатать:

1, 2, 3, 4, 
-1, -2, -3, -4, 
+0

Нельзя сказать никакого эффекта. Он имеет эффект, а именно отрицает целые числа. Однако он не сохраняется в памяти. –

+0

@JossieCalderon Я отредактировал свой ответ, надеюсь, теперь его яснее. – BugShotGG

Смежные вопросы