¿Usar funciones anidadas es algo malo?

9

En una tarea reciente, terminé llamando a mis funciones de una manera fea, uglyReceipt(cashParser(cashInput()))el programa en sí funcionaba perfectamente, pero aún sentía que estaba haciendo algo mal.

Llamar a funciones como esta mala práctica y, de ser así, ¿qué debería hacer en su lugar?

Carl Groth
fuente
Posible duplicado de funciones anidadas; permitir o no?
mosquito
1
¿Son esas funciones anidadas o es un nido de llamadas a funciones? Si el último OP puede haber (re) inventado la programación funcional.
Marca de alto rendimiento
¿Qué te hace pensar que esto sería una mala práctica?
Ixrec
1
@gnat: Esa pregunta no tiene ninguna relación. Esa pregunta trata sobre definiciones de funciones anidadas léxicamente, esta pregunta trata sobre pasar el resultado de una llamada de función como argumento a otra llamada de función.
Jörg W Mittag
Leí mal - gracias por señalar @ JörgWMittag (voto retraído)
mosquito

Respuestas:

11

Esto realmente depende de la cantidad de anidamiento que uses. Después de todo, puede usar resultados de funciones directamente en expresiones para mejorar la legibilidad. Tanto el código que no usa expresiones anidadas (como el código ensamblador) como el código que usa demasiadas expresiones anidadas son difíciles de leer. Un buen código trata de encontrar un equilibrio entre los extremos.

Así que veamos algunos ejemplos. El que diste en tu pregunta me parece bastante legítimo, así que no hay de qué preocuparse aquí. Sin embargo, una línea como

foo(bar(baz(moo, fab), bim(bam(ext, rel, woot, baz(moo, fab)), noob), bom, zak(bif)));

Definitivamente no sería tolerable. Del mismo modo, código como

double xsquare = x*x;
double ysquare = y*y;
double zsquare = z*z;
double xysquare = xsquare + ysquare;
double xyzsquare = xysquare + zsquare;
double length = sqrt(xyzsquare);

tampoco sería muy legible. sqrt(x*x + y*y + z*z)es mucho más fácil de entender, aunque combina un total de seis operaciones diferentes en una sola expresión.

Mi consejo es que prestes atención a las expresiones que aún puedes analizar fácilmente en tu cabeza. En el momento en que necesita echar un segundo vistazo para comprender lo que hace una sola expresión, es hora de introducir una variable adicional.

cmaster - restablecer monica
fuente
4

Creo que si es bueno o malo depende mucho del contexto. La razón principal por la que podría considerarse malo es que (posiblemente) hace que el código sea más difícil de leer y depurar. Esto es especialmente cierto cuando estás aprendiendo a programar por primera vez. A medida que sus habilidades de codificación (y código) se vuelven más avanzadas, hay momentos en que esto es aceptable.

Por ejemplo, considere un mensaje de error como este:

line 1492: missing argument "foo"

¿Cómo sabes si el argumento que falta es cashInput, cashParsero uglyReceipt? Dependiendo del idioma, el mensaje de error puede indicarle, pero puede que no.

Si separara esas llamadas de función, y el mensaje de error todavía lo señalara a la línea 1492, sabría instantáneamente dónde reside el problema:

1491: input = cashInput()
1492: parsed_value = cashParser(input)
1493: receipt = uglyReceipt(parsed_value)

Con los pasos separados por separado, es mucho más fácil depurar, ya que es posible establecer un punto de interrupción en cualquier paso, y puede inyectar valores fácilmente cambiando el valor de las variables locales.

Bryan Oakley
fuente
3

El concepto que subyace a su pregunta es tan importante que creo que necesita otra respuesta en lugar de solo un comentario (como había comenzado a hacer).

Las otras 3 respuestas hasta ahora proporcionan algunos puntos útiles de consideración sobre si una situación dada merece usar lo que llama "llamadas de función anidadas". Pero tal vez un punto más importante está oculto en los comentarios bajo su pregunta: en caso de que se haya perdido la sutileza de lo que sugieren esas personas eruditas, Carl, ha descubierto por sí mismo el tema realmente llamado programación funcional . Si nunca ha visto el término, es posible que no haya pensado que realmente era una "cosa" en el comentario de @ HighPerformanceMark.

Pero de hecho lo es! La programación funcional se ha escrito durante décadas, desde el artículo seminal de John Hughes Why Functional Programming Matters . Hay algunos lenguajes que son lenguajes funcionales (es decir, solo le permiten escribir en un estilo de programación funcional), lenguajes como Erlang, Lisp, OCaml o Haskell. Pero hay muchos más idiomas que son lenguajes híbridos imperativos / funcionales. Es decir, son lenguajes tradicionalmente imperativos, pero también ofrecen cierto soporte para la programación funcional, incluidos Perl, C ++, Java, C # y muchos más. La entrada de Wikipedia sobre programación funcional proporciona una buena sección que muestra una comparación del estilo funcional versus el estilo imperativo para varios idiomas.

Hay mucho que decir sobre las diferencias entre los estilos imperativos y funcionales, pero el punto de partida clave es que con la programación funcional, las funciones o los métodos no tienen efectos secundarios , por lo que en general es más fácil de entender y depurar programas.

Para leer más, también puede echar un vistazo a Por qué importa "Por qué la programación funcional importa" de Reginald Braithwaite y otra publicación interesante aquí sobre SO, ¿Por qué los lenguajes funcionales?

Michael Sorens
fuente
2

No es absolutamente una mala práctica en general. Las funciones llaman a aceptar valores y una forma de producir un valor es llamar a otra función.

Cuando veo que se define una variable, como:

parsed_value = cashParser(input)

... Tengo que considerar que parsed_valuepodría usarse más de una vez y probablemente tendré que verificar si esto es cierto o no (¿qué pasa si mi cambio rompe algo en otro lugar?). Cuando comienza a agregar más variables, puede ser más complejo para el lector hacer un seguimiento de todas ellas y cómo están relacionadas. Así que me siento aliviado cuando veo:

receipt = uglyReceipt(cashParser(input))

... porque el alcance / vida útil del valor intermedio es obvio. Ahora, como siempre, dividir una expresión larga en declaraciones separadas podría ayudar, especialmente si un nombre de variable puede dar más precisión sobre el propósito de un valor:

user_name = input()
volcado de memoria
fuente