¿Por qué algunas funciones muy larga? (Ideas necesarias para una investigación académica!) [Cerrada]

StackOverflow https://stackoverflow.com/questions/1127588

Pregunta

Estoy escribiendo un pequeño proyecto de investigación académica sobre las funciones extremadamente largas. Obviamente, yo no busco a ejemplos de mala programación , pero para los ejemplos de 100, 200 y 600 líneas de funciones de longitud que tiene sentido.

Yo estaré investigando la fuente del kernel Linux a través de un guión escrito por un Maestría escrito en el Universidad hebrea, que mide diferentes parámetros como el número de líneas de código, función de complejidad (medido por MCC) y otras golosinas. Por cierto, es un estudio bueno de análisis de código, y un material de lectura recomendada.

Me interesa si puede pensar en ninguna buena razón por la cual cualquier función debe ser excepcionalmente larga? Voy a estar buscando en C, pero los ejemplos y argumentos de cualquier idioma sería de gran utilidad.

¿Fue útil?

Solución

Puede que coger críticas por esto, pero la legibilidad. Una ejecución altamente serie, pero independiente que se podría dividir en función de N llama (de las funciones que se utilizan en ninguna otra parte) en realidad no se beneficiarán de la descomposición. A menos que cuentes encontrarse con un máximo arbitrario en función de la longitud como un beneficio.

Yo prefiero desplazo por bloques de tamaño N de función de código con el fin de navegar todo el archivo, golpeando las funciones N.

Otros consejos

Muchos de los valores en una sentencia switch?

Cualquier cosa genera a partir de otras fuentes, es decir, una máquina de estados finitos de un generador de analizadores sintácticos o similar. Si no se destina al consumo humano, estéticas o mantenibilidad preocupaciones son irrelevantes.

Las funciones pueden ser más largas en el tiempo, sobre todo si son modificados por muchos grupos de desarrolladores.

El caso en cuestión: Hace poco (~ 1 año o hace 2) refactorizado algún código de procesamiento de imágenes legado a partir de 2001 o así que contenía unos varios miles de Hoja de funciones. No pocos de varios miles de Hoja de archivos -. Unos varios miles de Hoja de funciones

A través de los años se añadió tanta funcionalidad a ellos sin llegar a poner en el esfuerzo de refactorizar adecuadamente.

Lea el capítulo en el Código de McConnell completa acerca de las subrutinas, que tiene pautas e indicaciones de cuándo debe romper cosas en funciones. Si tiene algún algoritmo, donde no se aplican esas reglas, que puede ser una buena razón para tener una función de tiempo.

El código generado puede generar funciones muy muy largos.

Los únicos que he codifiqué recientemente son en donde no se consigue mucho para hacerlos más pequeños, o puede hacer que el código sea menos legible. La idea de que una función que es sobre una cierta longitud es de alguna manera intrínsecamente mala es simplemente un dogma ciego. Al igual que cualquier dogma aplicado ciegamente alivia ot el seguidor de la necesidad de pensar realmente acerca de lo que se aplica en cualquier caso dado ...

Entre los ejemplos recientes ...

de análisis y validación de un archivo de configuración con una simple estructura de nombre = valor en una matriz, la conversión de cada valor cuando lo encuentro, esta es una sentencia switch masiva, un caso por cada opción de configuración. ¿Por qué? Podría haber dividido en un montón de llamadas a funciones triviales 5/6 de línea. Eso sería añadir unos 20 miembros privados de la clase. Ninguno de ellos son reutilizados en otro sitio. Factoring en trozos más pequeños simplemente no añadía valor suficiente para merecer la pena, así que ha sido el mismo desde entonces el prototipo. Si quiero otra opción, añadir otro caso.

Otro caso es el código de comunicación con el cliente y el servidor en la misma aplicación, y su cliente. Un montón de llamadas para leer / escribir cualquiera de los cuales puede fallar, en cuyo caso la fianza y devuelven false. Por lo que esta función es básicamente lineal, y tiene puntos de fianza (si no, volver) después de que casi todas las llamadas. Una vez más, no hay nada que ganar con lo que es más pequeño y no hay manera de realmente por ello es más pequeño.

También me gustaría añadir que la mayoría de mis funciones son un par de "pantallas completas" y me esfuerzo en las áreas más complejas para mantenerlo a una "pantalla completa", simplemente porque puedo mirar a toda la función a la vez. Está bien para las funciones que son básicamente de naturaleza lineal y no tienen muchos bucles complejos o condiciones que van en lo que el flujo es simple. Como nota final, prefiero aplicar el razonamiento de costes y beneficios al momento de decidir qué código refactorizar, y priorizar en consecuencia. Ayuda a evitar el proyecto perpetuamente a medio terminar.

A veces me encuentro escribiendo un archivo plano (para su uso por parte de terceros) que implica cabeceras, remolques, y los registros de detalles que están vinculados. Es más fácil tener una función de tiempo con el fin de computación resúmenes de lo que es idear algún esquema para pasar valores de un lado a otro a través de una gran cantidad de funciones pequeñas.

Un punto que creo que tiene un cojinete es que los diferentes lenguajes y herramientas tienen diferente ámbito léxico asociado a funciones.

Por ejemplo, Java permite suprimir las advertencias con una anotación. Puede ser deseable limitar el alcance de la anotación y así mantener la función corta para ese propósito. En otro idioma, rompiendo esa sección en su propia función podría ser completamente arbitraria.

Polémica: En JavaScript, que tienden a crear únicamente funciones con el propósito de la reutilización de código. Si un fragmento sólo se ejecuta en un solo lugar, me resulta onerosa para saltar alrededor del archivo (s) después de los espaguetis de las referencias de función. Creo que facilitan el cierre y por lo tanto refuerzan [padre] deja de funcionar. Desde JS es un lenguaje interpretado y el código real se envía a través del cable, es bueno para mantener la longitud del código pequeña - crear declaraciones a juego y las referencias no ayuda (esto podría ser considerado como una optimización prematura). Una función tiene que ser bastante larga en JS antes de que decida trocean con el expreso propósito de "mantener las funciones cortas".

De nuevo en JS, a veces toda la 'clase' es técnicamente una función con muchos sub-funciones cerrados pero hay herramientas para ayudar a lidiar con ella.

Por otro lado en JS, las variables tienen el alcance para la longitud de la función y así que es un factor que puede limitar la longitud de una función dada.

Las funciones muy largas que me encuentro no están escritas en C, por lo que tendrá que decidir si esto se aplica a su investigación o no. Lo que tengo en mente son algunas de las funciones de PowerBuilder que son varios cientos de líneas de largo, siendo tan por las razones siguientes:

  • Han sido escrito hace más de 10 años, por personas que en ese momento no contaban con los estándares de codificación en mente.
  • El entorno de desarrollo hace que sea un poco más difícil de crear funciones. Apenas una buena excusa, pero es una de esas pequeñas cosas que a veces le desanima a trabajar correctamente, y supongo que alguien acaba de conseguir perezoso.
  • Las funciones han evolucionado con el tiempo, añadiendo el código y complejidad.
  • Las funciones contienen enormes bucles, cada iteración, posiblemente, el manejo de diferentes tipos de datos de una manera diferente. Utilizando decenas (!) De las variables locales, algunas variables miembro y algunas variables globales, se han vuelto extremadamente complejo.
  • Al ser tan viejo y feo, nadie se atreve a ellos refactorización en partes más pequeñas. Tener tantos casos especiales manejados en ellas, éstas se descompongan es buscar problemas.

Este es otro lugar donde las prácticas de programación obvias malas cumplen con la realidad. Mientras que cualquier estudiante de primer año de CS podría decir esas bestias son malas, nadie gastar dinero en hacer que se vean más bonita (dado que al menos por ahora, que dan a luz).

Con mucho, la más común que veo / escritura son declaraciones de cambio largas o sentencias if / else semi-switch para tipos que no pueden ser aplicadas en los estados de conmutación de este idioma (ya se ha mencionado un par de veces). El código generado es un caso interesante, pero estoy centrado en código escrito humana aquí. En cuanto a mi proyecto actual, la función sólo es verdaderamente larga no incluidos anteriormente (296 COL / 650 LOT) hay un código de vaquero que estoy usando como una evaluación temprana de la salida de un generador de código Voy a utilizar en el futuro. Sin duda me refactorización, que lo elimina de la lista.

Hace muchos años, yo estaba trabajando en algún tipo de software de computación científica que tenía una función mucho en ella. El método utiliza un gran número de variables locales y refactorización el método mantuvo resultando en una diferencia medible por perfilado. Incluso una mejora del 1% en esta sección del código salvó horas de tiempo de cálculo, por lo que la función se quedaba mucho tiempo. He aprendido mucho desde entonces, así que no puedo hablar de lo que me ocupe de la situación actual.

Velocidad:

  • Llamar a una función significa que empuja a la pila, a continuación, saltar, a continuación, almacenar en la pila de nuevo, a continuación, saltar de nuevo. si utiliza los parámetros de la función, por lo general tienen varios empujes más.

Considere un bucle:

for...
   func1

dentro de un bucle, todos esos empujones y saltos puede ser un factor.

Esto se resolvió en gran medida con la presentación de funciones en línea en C99 y no oficial antes de eso, pero algo de código escrito antes, o se creó con la compatibilidad en mente, puede haber sido durante mucho tiempo para que la razón.

También Inline tiene su fluye, algunos se describen en las href="http://en.wikipedia.org/wiki/Inline_function" enlazan .

Editar

Como un ejemplo de cómo una llamada a una función puede hacer un programa más lenta:

4         static void
5 do_printf()
6 {
7         printf("hi");
8 }
9         int
10 main()
11 {
12         int i=0;
13         for(i=0;i<1000;++i)
14                 do_printf();
15 }

Esto produce (GCC 4.2.4):

 .
 . 
 jmp    .L4
 .L5:
call    do_printf
addl    $1, -8(%ebp)
 .L4:
cmpl    $999, -8(%ebp)
jle .L5

 .
 .
do_printf:
pushl   %ebp
movl    %esp, %ebp
subl    $8, %esp
movl    $.LC0, (%esp)
call    printf
leave
ret

en contra:

         int
 main()
 {
         int i=0;
         for(i=0;i<1000;++i)
                 printf("hi");
 }

o en contra de:

 4         static inline void __attribute__((always_inline)) //This is GCC specific!
 5 do_printf()
 6 {
 7         printf("hi");
 8 }

Ambos producen (GCC 4.2.4):

jmp .L2
.L3:
movl    $.LC0, (%esp)
call    printf
addl    $1, -8(%ebp)
.L2:
cmpl    $999, -8(%ebp)
jle .L3

¿Qué es más rápido.

código de análisis XML a menudo tiene resmas de procesamiento carácter de escape en una función de configuración.

Las funciones con las que trato (no escribir) a ser largo, porque se expandió y se expandió y nadie pasa el tiempo para re-factor de las funciones. Ellos sólo siguen añadiendo la lógica de las funciones sin pensar en el cuadro grande.

Yo trato con mucho desarrollo Cut-n-goma ...

Por lo tanto, para el papel, un aspecto a tener en cuenta es la falta de mantenimiento del plan / ciclo, etc.

Algunas ideas que no se mencionan explícitamente aún:

  • tareas repetitivas, por ejemplo la función lee una tabla de base de datos con 190 columnas y tiene a la salida como un archivo plano (suponiendo que las columnas deben ser tratados de forma individual, por lo que un simple bucle sobre todas las columnas no lo harán). Por supuesto, se podría crear 19 funciones, cada uno dar salida 10 columnas, pero eso no haría que el programa mejor.
  • , APIs detallados complicados, como de Oracle OCI . Cuando aparentemente simples acciones requieren grandes cantidades de código, es difícil dividirlo en pequeñas funciones que tienen sentido.
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top