¿Crear funciones anidadas por razones puramente estéticas?

16

Siempre me he preguntado qué piensan otros programadores sobre la idea de crear funciones estéticas puras.

Decir que tengo una función que procesa un fragmento de datos: Function ProcessBigData. Digo que necesito varias etapas del proceso, sólo es válida para que los datos: Step1, Step2, Step3.

El enfoque normal que más veo en el código fuente es escribir comentarios así:

Function ProcessBigData:
    # Does Step1
    Step1..
    Step1..

    #Does Step2
    Step2..
    Step2..

Lo que suelo hacer, pero siempre me sentí mal debido a la falta de ese estilo de codificación por parte de otros compañeros es:

Function ProcessBigData:
    Function Step1:
        Step1..
        Step1..

    Function Step2:
        Step2..
        Step2..

    Step1() -> Step2()

Me preocupa principalmente si hay inconvenientes para ese estilo en Javascript y Python

¿Hay alguna alternativa que no esté viendo?

Slytael
fuente
3
No puedo decir nada sobre Python, pero para Javascript, hay un costo de rendimiento para las funciones anidadas: la mayoría de los motores JavaScript usan una estructura similar a una lista vinculada para representar el alcance variable. Agregar una capa adicional de funciones obliga al motor a buscar posiblemente una estructura de datos más larga / más grande al resolver variables. Por otro lado, la raíz de todo mal es, por supuesto, la optimización prematura. :)
Marco

Respuestas:

4

No es tan extraño como podrías pensar. Por ejemplo, en ML estándar se acostumbra limitar el alcance de las funciones auxiliares. De acuerdo, SML tiene sintaxis para facilitarlo:

local
    fun recursion_helper (iteration_variable, accumulator) =
        ... (* implementation goes here *)
in
    fun recursive_function (arg) = recursion_helper(arg, 0);
end

Consideraría este buen estilo, dado que 1) las funciones pequeñas facilitan el razonamiento sobre el programa, y ​​2) le indica al lector que estas funciones no se usan fuera de ese alcance.

Supongo que es posible que haya algo de sobrecarga en la creación de las funciones internas cada vez que se llama a la función externa (no sé si JS o Python optimizan eso) pero ya sabes lo que dicen sobre la optimización prematura.

Doval
fuente
11

Por lo general, es bueno hacer esto siempre que sea posible, pero me gusta pensar en este tipo de trabajo no como "pasos", sino como subtareas .

Una subtarea es una unidad de trabajo específica que se puede hacer: tiene una responsabilidad específica y entradas y salidas definidas (piense en la "S" en SÓLIDO ). Una subtarea no necesita ser reutilizable: algunas personas tienden a pensar "Nunca tendré que llamar a esto desde otra cosa, entonces ¿por qué escribirlo como una función?" Pero eso es una falacia.

Intentaré también describir los beneficios y también cómo se aplica a las funciones anidadas (cierres) frente a solo otra función de la clase. En términos generales, recomendaría no usar cierres a menos que lo necesite específicamente (hay muchos usos, pero separar el código en fragmentos lógicos no es uno de ellos).

Legibilidad.

Más de 200 líneas de código de procedimiento (cuerpo de una función) son difíciles de leer. Las funciones de 2 a 20 líneas son fáciles de leer. El código es para humanos.

Anidado o no, en su mayoría obtiene el beneficio de la legibilidad, a menos que esté utilizando muchas variables del ámbito primario, en cuyo caso puede ser tan difícil de leer.

Limitar alcance variable

Tener otra función lo obliga a limitar el alcance variable y específicamente a pasar lo que necesita.

Esto a menudo también hace que el código de estructura sea mejor, porque si necesita algún tipo de variable de estado de un "paso" anterior, es posible que en realidad haya otra subtarea que debería escribirse y ejecutarse primero para obtener ese valor. O, en otras palabras, hace que sea más difícil escribir fragmentos de código altamente acoplados.

Tener funciones anidadas le permite acceder a variables en el ámbito primario desde el interior de la función anidada (cierre). Esto puede ser muy útil, pero también puede conducir a errores sutiles y difíciles de encontrar, ya que la ejecución de la función puede no ocurrir en la forma en que está escrita. Este es aún más el caso si está modificando variables en el ámbito primario (una muy mala idea, en general).

Pruebas unitarias

Cada subtarea, implementada una función (o incluso una clase) es una pieza de código independiente y comprobable. Los beneficios de las pruebas unitarias y TDD están bien documentados en otros lugares.

El uso de funciones / cierres anidados no permite pruebas unitarias. Para mí, esto es un factor decisivo y la razón por la que debería ser otra función, a menos que haya una necesidad específica de un cierre.

Trabajando en equipo / Diseño de arriba hacia abajo

Las subtareas pueden ser escritas por diferentes personas, independientemente, si es necesario.

Incluso solo, puede ser útil cuando se escribe código simplemente invocar alguna subtarea que aún no existe, mientras se construye la funcionalidad principal, y preocuparse por implementar la subtarea solo después de saber que obtendrá los resultados que necesita en un de manera significativa. Esto también se llama diseño / programación de arriba hacia abajo.

Reutilización de código

Bien, a pesar de lo que dije antes, a veces en realidad termina siendo una razón más tarde para reutilizar una subtarea para otra cosa. No estoy abogando en absoluto por el "astronauta de la arquitectura", sino que al escribir código débilmente acoplado, puede terminar beneficiándose más tarde de la reutilización.

A menudo, esa reutilización significa una refactorización, lo cual es perfectamente esperado, pero refactorizar los parámetros de entrada a una pequeña función independiente es MUCHO más fácil que extraerlo de una función de más de 200 meses después de que se escribió, lo cual es realmente mi punto aquí.

Si usa una función anidada, reutilizarla generalmente es una cuestión de refactorizar a una función separada de todos modos, lo cual nuevamente, es por eso que argumentaría que anidar no es el camino a seguir.

Gregmac
fuente
2
Estos son algunos puntos realmente válidos para usar funciones en general, pero no obtuve de su respuesta si cree que las funciones ANIDADAS son una buena idea. ¿O obtienes las funciones en el alcance ascendente?
Slytael
Lo siento, buen punto, quedé atrapado en los otros beneficios que olvidé abordar en esa parte. :) Editado.
gregmac