2010-06-10 9 views
20

estoy tratando de averiguar si hay una beneficio computacional real a la utilización de las expresiones lambda en C++, es decir "este código se compila/corre más rápido/más lento debido a que usamos las expresiones lambda" o es sólo un beneficio de desarrollo limpio abierto para el abuso por codificadores pobres que intentan verse bien?¿Cuál es la motivación detrás de las expresiones lambda C++ 11?

Entiendo que esta pregunta puede parecer subjetiva, pero agradecería mucho la opinión de la comunidad sobre este asunto.

+17

voy a tomar "tercero excluido" por $ 500 Alex. –

+3

Entonces, las dos únicas razones que puede pensar para una función de idioma son "va más rápido" o "es como una raya rápida en un cohete de arroz".¿Cuál es el término latino elegante para "prueba por falta de imaginación" de nuevo? –

+0

Esto ha sido esclarecedor. Gracias a todos por las respuestas. – LoudNPossiblyWrong

Respuesta

41

No creo que sea tanto el rendimiento computacional como el aumento del poder expresivo del lenguaje.

+9

Un lenguaje de programación sin lambda es como un día sin luz solar. – Brian

+11

@Brian, vives en Seattle, no debería ser un gran cambio;) – JaredPar

+4

@Brian: entonces C++ con lambda es como vivir en el subsótano pero tener un televisor que muestra el sol brillando sobre un paisaje rural - ves la luz pero simplemente no siente lo mismo. –

8

Lambdas son azúcar sintáctico para las clases de functor, por lo que no, no hay beneficio computacional. En cuanto a la motivación, ¿probablemente alguna otra docena de lenguas populares que tienen lambdas en ellas?

Uno podría argumentar que ayuda en la legibilidad del código (teniendo su functor declarado en línea donde se usa).

9

IMO, lo más importante acerca de lambda es que mantiene el código relacionado muy cerca. Si usted tiene este código:

std::for_each(begin, end, unknown_function); 

que necesita para navegar a unknown_function para entender lo que hace el código. Pero con una lambda, la lógica puede mantenerse junta.

+1

Idealmente, "función_desconocida" se nombraría lo suficientemente bien como para darle una buena idea de lo que hace. –

+5

@Fred: si tiene la definición lambda allí mismo en el punto de uso, el lector se ahorrará el esfuerzo de preguntarse si la función tiene un buen nombre o no, y el escritor se ahorrará el esfuerzo de tener que elegir un buen nombre para una operación trivial que solo se usará en un solo lugar. –

+0

@Jim Lewis: De acuerdo. No creo que las lambdas sean algo malo. Pero al mismo tiempo, no creo que sea siempre malo mover un cuerpo de bucle a un functor. Como siempre, usa lo mejor para tu situación. –

43

El beneficio es lo que es lo más importante en los programas informáticos de escritura: más fácil de entender el código. No estoy al tanto de ninguna consideración de rendimiento.

C++ permite, en cierta medida, hacer Programación funcional. Considere esto:

std::for_each(begin, end, doer); 

El problema con esto es que la función (objeto) doer

  1. especifica lo que se hace en el bucle
  2. aunque algo esconde lo que realmente se hace (que tiene para buscar la implementación del objeto de función operator())
  3. debe ser definido en a distinto alcance que el std::for_each llamada
  4. contiene una cierta cantidad de código repetitivo
  5. es a menudo código de usar y tirar que no se utiliza para nada más que éste bucle construir

Lambdas mejorar considerablemente el todo esto (y tal vez un poco más lo olvidé).

2

Bueno, comparar esto:

int main() { 
    std::vector<int> x = {2, 3, 5, 7, 11, 13, 17, 19}; 

    int center = 10; 
    std::sort(x.begin(), x.end(), [=](int x, int y) { 
     return abs(x - center) < abs(y - center); 
    }); 

    std::for_each(x.begin(), x.end(), [](int v) { 
     printf("%d\n", v); 
    }); 

    return 0; 
} 

con esto:

// why enforce this to be defined nonlocally? 
void printer(int v) { 
    printf("%d\n", v); 
} 

int main() { 
    std::vector<int> x = {2, 3, 5, 7, 11, 13, 17, 19}; 

    // why enforce we to define a whole struct just need to maintain a state? 
    struct { 
     int center; 
     bool operator()(int x, int y) const { 
      return abs(x - center) < abs(y - center); 
     } 
    } comp = {10}; 

    std::sort(x.begin(), x.end(), comp); 

    std::for_each(x.begin(), x.end(), printer); 

    return 0; 
} 
+4

No creo que el segundo sea legal. Los tipos AFAIK function-local no se deben usar como argumentos de plantilla. ¿O solucionó C++ 2003 este embarazoso problema? – sbi

+1

@sbi: ¿Plantillas? ¿Qué plantillas? De todos modos, la segunda pieza todavía está compilada en el modo C++ 0x (tenga en cuenta el uso de la lista de inicializadores), ya que el OP simplemente solicita la motivación de las expresiones lambda. – kennytm

+4

'std :: sort()' y 'std :: for_each()' son las plantillas referidas a @sbi. C++ 0x levanta esa restricción sin embargo. –

13

No hay ninguna ventaja de rendimiento per se, sino la necesidad de lambda se produjo como consecuencia de la amplia adopción de la STL y sus ideas de diseño.

Específicamente, los algoritmos STL hacen uso frecuente de funtores. Sin lambda, estos funtores deben declararse previamente para ser utilizados. Lambdas hace posible tener funcionadores "anónimos" en el lugar.

Esto es importante porque hay muchas situaciones en las que necesita usar un funtor solo una vez, y no desea darle un nombre por dos razones: no desea contaminar el espacio de nombres, y en esos casos específicos, el nombre que das es vago o extremadamente largo.

Yo, por ejemplo, uso mucho STL, pero sin C++ 0x utilizo mucho más para los bucles() que el algoritmo for_each() y sus primos. Eso es porque si tuviera que usar for_each() en su lugar, necesitaría obtener el código del circuito y declarar un functor para él. Además, no se podría acceder a todas las variables locales antes del bucle, así que necesitaría escribir código adicional para pasarlas como parámetros al constructor del functor u otra cosa equivalente. Como consecuencia, tiendo a no usar for_each() a menos que haya una gran motivación, de lo contrario el código sería más largo y más difícil de leer.

Eso es malo, porque es bien sabido que usar for_each() y algoritmos similares da mucho más espacio al compilador & la biblioteca para optimizaciones, incluido el paralelismo automático. Entonces, indirectamente, lambda favorecerá un código más eficiente.

+2

¡La mejor explicación de "C++ lambda en pocas palabras" que he encontrado hasta ahora! – hubeir

5

Aunque creo que otras partes de C++ 0x son more important, lambdas son más que "azúcar sintáctico" para C++ 98 objetos de función de estilo, ya que pueden capturar contextos, y lo hacen por su nombre y entonces pueden tomar esos contextos en otro lugar y ejecutar. Esto es algo nuevo, no algo que "compila más rápido/más lento".

#include <iostream> 
#include <vector> 
#include <functional> 
void something_else(std::function<void()> f) 
{ 
     f(); // A closure! I wonder if we can write in CPS now... 
} 
int main() 
{ 
     std::vector<int> v(10,0); 
     std::function<void()> f = [&](){ std::cout << v.size() << std::endl; }; 

     something_else(f); 
} 
+0

No, no es nuevo. Lambdas captura por nombre en el punto de definición. Exactamente como objetos de función con un constructor. Porque lambda * son * azúcar sintáctica para el objeto-función y podrían haberse implementado como una reescritura de fuente a fuente (ver http://blogs.msdn.com/b/vcblog/archive/2008/10/28/lambdas- auto-y-static-assert-c-0x-features-in-vc10-part-1.aspx para ejemplos de dicha reescritura). Bueno, ya no es posible en el borrador reciente debido a otra razón: el lambda sin estado ahora debe decaer para funcionar con el puntero, por lo que es un poco diferente ahora que el objeto de función simple, pero no para la parte de captura. –

+0

@Thomas Petit: objeto de función con un constructor no tiene ningún concepto de alcance en el punto de definición. Para reescribir automáticamente un [&] lambda como un funtor, uno tendría que examinar su fuente completa y examinar el origen completo del programa antes de la definición de lambda para resolver los tipos asociados con los nombres utilizados en el lambda. No es una transformación de sintaxis – Cubbi

+0

No es una transformación de sintaxis, sino una sintaxis más limpia para algo que podría hacerse previamente. De ahí el azúcar sintáctico. – jalf

2

"un desarrollo ordenado perk abierto para el abuso por parte de los codificadores pobres que intentan ir a la moda?" ... como se llame, tiene una gran cantidad de códigomás legible y fácil de mantener. No aumenta el rendimiento.

La mayoría de las veces, un programador itera sobre un rango de elementos (buscando un elemento, acumulando elementos, clasificando elementos, etc.). Usando el estilo funcional, inmediatamente verá lo que el programador intenta hacer, como diferente del uso de bucles for, donde todo "parece" lo mismo.

comparar algoritmos + lambda:

iterator longest_tree = std::max_element(forest.begin(), forest.end(), [height]{arg0.height>arg1.height}); 
iterator first_leaf_tree = std::find_if(forest.begin(), forest.end(), []{is_leaf(arg0)}); 
std::transform(forest.begin(), forest.end(), firewood.begin(), []{arg0.trans(...)); 
std::for_each(forest.begin(), forest.end(), {arg0.make_plywood()}); 

con oldschool de bucles;

Forest::iterator longest_tree = it.begin(); 
for (Forest::const_iterator it = forest.begin(); it != forest.end(); ++it{ 
    if (*it.height() > *longest_tree.height()) { 
    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); 
} 

(sé que esto pieace de código contiene errores sintácticos.)

Cuestiones relacionadas