¿Cuál es la diferencia entre std::transform y std::para cada uno?


Ambos se pueden usar para aplicar una función a un rango de elementos.

En un nivel alto:

  • std::for_each ignora el valor devuelto de la función, y garantiza la orden de ejecución.
  • std::transform asigna el valor devuelto al iterador, y no no garantiza la orden de ejecución.

¿Cuándo prefiere usar el uno frente al otro? ¿Hay alguna advertencia sutil?

Author: Null, 2015-06-26

4 answers

std::transform es lo mismo que map. La idea es aplicar una función a cada elemento entre los dos iteradores y obtener un contenedor diferente compuesto por elementos resultantes de la aplicación de dicha función. Es posible que desee usarlo para, por ejemplo, proyectar el miembro de datos de un objeto en un nuevo contenedor. A continuación, std::transform se utiliza para transformar un contenedor de std::string s en un contenedor de std::size_ts.

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();});

Por otro lado, se ejecuta std::for_each para los efectos secundarios únicos. En otros palabras, std::for_each se asemeja mucho a un bucle for basado en rango simple.

Volver al ejemplo de cadena:

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

De hecho, a partir de C++11 se puede lograr lo mismo con una notación terser usando bucles basados en rangos for:

for (std::size_t name_size: name_sizes) {
    std::cout << name_size << std::endl;
}
 32
Author: Ilio Catallo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-06 05:11:45

Su visión general de alto nivel

  • std::for_each ignora el valor devuelto de la función y garantiza el orden de ejecución.
  • std::transform asigna el valor devuelto al iterador, y no garantiza el orden de ejecución.

Prácticamente lo cubre.

Otra forma de verlo (preferir uno sobre el otro);

  • ¿Importan los resultados (el valor devuelto) de la operación?
  • Es el operación en cada elemento un método miembro sin valor de retorno?
  • hay dos rangos de entrada?

Una cosa más a tener en cuenta (sutil advertencia) es el cambio en los requisitos de las operaciones de std::transform antes y después de C++11 (desde en.cppreference.com);

  • Antes de C++11, estaban obligados a"no tener ningún efecto secundario",
  • Después de C++11, esto cambió a " no debe invalidar ningún iterador, incluidos los iteradores finales, o modificar cualquier elemento de los rangos involucrados "

Básicamente se trataba de permitir la orden de ejecución indeterminada.

¿Cuándo uso uno sobre el otro?

Si quiero manipular cada elemento en un rango, entonces uso for_each. Si tengo que calcular algo de cada elemento, entonces usaría transform. Cuando uso for_each y transform, normalmente los emparejo con una lambda.

Dicho esto, encuentro mi uso actual del tradicional for_each disminuyendo algo desde el advenimiento del rango basado en for bucles y lambdas en C++11 (for (element : range)). Encuentro su sintaxis e implementación muy naturales (pero su kilometraje aquí variará) y un ajuste más intuitivo para algunos casos de uso.

 14
Author: Niall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-09 09:28:30

Aunque la pregunta ha sido respondida, creo que este ejemplo aclararía aún más la diferencia.

for_each pertenece a operaciones STL no modificables, lo que significa que estas operaciones no cambian elementos de la colección o de la colección misma. Por lo tanto, el valor devuelto por for_each siempre se ignora y no se asigna a un elemento de colección. Sin embargo, todavía es posible modificar elementos de la colección, por ejemplo, cuando un elemento es pasado a la función f usando referencia. Uno debe evitar tal comportamiento, ya que no es consistente con los principios STL.

En contraste, la función transform pertenece a modificando operaciones STL y aplica predicados dados (unary_op o binary_op) a elementos de la colección o colecciones y el almacén da como resultado otra colección.

#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;
}

Que imprimirá:

1, 2, 3, 4, 
-1, -2, -3, -4, 
 9
Author: BugShotGG,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-27 09:38:21

El ejemplo real de usar std:: tranform es cuando desea convertir una cadena a mayúsculas, puede escribir código como este:

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

Si intenta lograr lo mismo con std::for_each como:

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

No lo convertirá en cadena mayúscula

 0
Author: mystic_coder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-26 06:50:04