Articulo de referencia

Función anidada

En programación informática , una función anidada (o procedimiento anidado o subrutina ) es una función con nombre definida dentro de otro bloque (que la contiene) y cuyo ámbito...

En programación informática , una función anidada (o procedimiento anidado o subrutina ) es una función con nombre definida dentro de otro bloque (que la contiene) y cuyo ámbito léxico se limita a dicho bloque . Esto significa que solo se puede invocar por su nombre dentro del cuerpo del bloque que la contiene y puede utilizar identificadores declarados en bloques externos , incluidas funciones externas. El bloque que la contiene suele ser, aunque no siempre, otra función.

La compatibilidad de los lenguajes de programación con las funciones anidadas varía. En el caso de los lenguajes de programación estructurada , se admite en algunos lenguajes obsoletos como ALGOL , Simula 67 y Pascal , así como en el popular JavaScript . También se admite comúnmente en lenguajes dinámicos y funcionales . Sin embargo, no se admite en algunos lenguajes de uso común, como C y C++ estándar .

Otras tecnologías de programación ofrecen beneficios similares. Por ejemplo, una función lambda permite definir una función dentro de otra (y en cualquier otro lugar), además de ofrecer una ocultación y encapsulación de datos similares. Cabe destacar que una función lambda no tiene nombre (es anónima) y, por lo tanto, no se puede invocar por su nombre ni tiene visibilidad.

Atributos

El ámbito de una función anidada es el bloque que la contiene , ya sea un bloque de función o un bloque dentro del cuerpo de una función. No es visible (no se puede llamar por su nombre) fuera de su bloque contenedor.

Una función anidada puede usar identificadores (es decir, el nombre de funciones, variables, tipos, clases) declarados en cualquier bloque contenedor, excepto cuando estén enmascarados por declaraciones internas con los mismos nombres.

Una función anidada puede declararse dentro de otra función anidada, de forma recursiva, para formar una estructura profundamente anidada. Una función profundamente anidada puede acceder a los identificadores declarados en todos sus bloques contenedores, incluidas las funciones que la contienen.

En ciertas situaciones, las funciones anidadas pueden dar lugar a la creación de un cierre . Si la función anidada puede escapar de la función contenedora (por ejemplo, si las funciones son objetos de primera clase y una función anidada se pasa a otra función o es devuelta por la función contenedora), se crea un cierre y las llamadas a esta función pueden acceder al entorno de la función original. El marco de la función contenedora inmediata debe permanecer activo hasta que finalice el último cierre que la referencia. Por lo tanto, las variables automáticas no locales referenciadas en los cierres no pueden asignarse en la pila en lenguajes que permiten que el cierre persista más allá de la vida útil del bloque contenedor. Esto se conoce como el problema de los argumentos de funciones y es una razón clave por la que las funciones anidadas no se implementaron en algunos lenguajes más simples, ya que complica significativamente la generación y el análisis del código, especialmente cuando las funciones están anidadas a varios niveles, compartiendo diferentes partes de su entorno.

Valor

La tecnología de funciones anidadas permite al programador escribir código fuente con atributos beneficiosos como la ocultación de información , la encapsulación y la descomposición . El programador puede dividir una tarea en subtareas que solo tienen sentido dentro del contexto de la tarea principal, de modo que las funciones de las subtareas permanecen ocultas para quienes las llaman pero no están diseñados para utilizarlas.

El alcance de los bloques permite que las funciones compartan el estado de los bloques que los contienen (incluidas las funciones que los contienen) sin pasar parámetros ni usar variables globales . [ 1 ]

Usos

Ayudante

Una función anidada suele actuar como una función auxiliar o una función recursiva .

Flujo de control

Las funciones anidadas se pueden usar para el control de flujo no estructurado , mediante la instrucción `return` para el control general no estructurado. Esto permite un control más preciso que el que ofrecen otras características integradas del lenguaje; por ejemplo, permite la terminación anticipada de un bucle `for` si breakno está disponible, o la terminación anticipada de un bucle `for` anidado si no se dispone de un bucle multinivel breako de excepciones.

Funciones de orden superior

En algunos lenguajes, es posible crear una función anidada que acceda a un conjunto de parámetros de la función externa (un cierre ) y que dicha función sea el valor de retorno de la función externa. De este modo, es posible devolver una función configurada para realizar una tarea determinada con pocos o ningún parámetro adicional, lo que puede aumentar el rendimiento de forma significativa. [ 2 ]

Ejemplos

Ejemplo sencillo

Un ejemplo sencillo en Pascal:

función E ( x : real ) : real ; función F ( y : real ) : real ; inicio F := x + y fin ; inicio E := F ( 3 ) + F ( 4 ) fin ;

La función Festá anidada dentro de E. Tenga en cuenta que Eel parámetro de xtambién es visible en F(ya que Fes parte de E) mientras que tanto xcomo yson invisibles fuera de Ey Frespectivamente.

De manera similar, en Standard ML :

fun e ( x : real ) = let fun f y = x + y in f 3 + f 4 end ;

En Haskell :

e :: Float -> Float e x = f 3 + f 4 donde f y = x + y

En PL/I :

e: procedimiento(x) devuelve(flotante); declarar x flotante; f: procedimiento(y) devuelve(flotante); declarar y flotante; devolver x + y fin; devolver f(3.0) + f(4.0); fin; 

En Python :

def e ( x : float ) -> float : def f ( y : float ) -> float : return x + y return f ( 3.0 ) + f ( 4.0 )

En GNU C [ 3 ] que extiende el estándar C con funciones anidadas:

float e ( float x ) { float f ( float y ) { return x + y ; } return f ( 3.0f ) + f ( 4.0f ); }

Ordenación rápida

La siguiente es una implementación de quicksort : [ 4 ]

void sort ( int * items , int size ) { void quickSort ( int first , int last ) { void swap ( int p , int q ) { int tmp = items [ p ]; items [ p ] = items [ q ]; items [ q ] = tmp ; } int partition () { int pivot = items [ first ]; int index = first ; swap ( index , last ); for ( int i = first ; i < last ; i ++ ) { if ( items [ i ] < pivot ) { swap ( index ++ , i ); } } swap ( index , last ); return index ; }if ( first < last ) { int pivotIndex = partition (); quickSort ( first , pivotIndex - 1 ); quickSort ( pivotIndex + 1 , last ); } } quickSort ( 0 , size - 1 ); }

A continuación se muestra una implementación del algoritmo de ordenación rápida basado en particiones de Hoare utilizando la sintaxis de expresiones lambda de C++11 , que es una tecnología alternativa que también permite ocultar una función dentro de otra:

// Iter representa una plantilla de iterador de acceso aleatorio < typename Iter > void sort ( Iter begin , Iter end ) { auto partition = [ & ]() -> Iter { // Esquema de partición de Hoare Iter & pivot = * begin ; Iter forwardCursor = begin ; Iter backwardCursor = end - 1 ; Iter partitionPositionFound = false ;auto locatePartitionPosition = [ & ]() -> void { while ( * forwardCursor < pivot ) { ++ forwardCursor ; } while ( pivot < * backwardCursor ) { -- backwardCursor ; } if ( forwardCursor >= backwardCursor ) { partitionPositionFound = true ; } else swap ( * forwardCursor , * backwardCursor ); } };// Función auxiliar trivial auto moveOnAndTryAgain = [ & ]() -> void { ++ forwardCursor ; -- backwardCursor ; };// Breve descripción del proceso de partición real while ( true ) { locatePartitionPosition (); if ( partitionPositionFound ) return backwardCursor + 1 ; else { moveOnAndTryAgain (); } } };// Breve descripción del algoritmo quicksort if ( begin < end - 1 ) { Iter partitionPosition = partition (); sort ( begin , partitionPosition ); sort ( partitionPosition , end ); } }

Idiomas

Entre los lenguajes de programación que admiten funciones anidadas, destacan los siguientes:

Lenguajes funcionales

En la mayoría de los lenguajes de programación funcional , como Scheme, las funciones anidadas son una forma común de implementar algoritmos con bucles. Se crea una función interna recursiva simple ( de cola ) que actúa como el bucle principal del algoritmo, mientras que la función externa realiza acciones iniciales que solo deben ejecutarse una vez. En casos más complejos, se pueden crear varias funciones recursivas entre sí como funciones internas.

Alternativas

Se pueden utilizar diversas técnicas alternativas para lograr resultados de programación similares a los obtenidos mediante funciones anidadas.

Modularidad

Una alternativa común es aprovechar la tecnología de modularidad del lenguaje. Algunas funciones están disponibles para su uso fuera del módulo, mientras que otras solo son visibles dentro del mismo.

En C, esto se puede implementar declarando funciones y variables como estáticas para ocultarlas del código fuera del archivo. [ 10 ] Esto permite ocultar, encapsular y descomponer datos, pero con un nivel de granularidad diferente al de las funciones anidadas. Esta modularidad no admite más de un nivel de anidamiento.

En los lenguajes orientados a objetos , una clase generalmente proporciona un ámbito en el que las funciones y el estado pueden permanecer ocultos para quienes usan la clase, pero ser accesibles dentro de ella. Algunos lenguajes permiten anidar clases.

Parámetros

Para implementar la ocultación de datos, las funciones pueden pasar datos compartidos como parámetros, pero esto aumenta la complejidad de las llamadas a funciones. [ 1 ]

En C, esto generalmente se implementa pasando un puntero a una estructura que contiene los datos compartidos. [ 10 ]

Lambda

En PHP y otros lenguajes, la función lambda es una alternativa. Una función se define en una instrucción de código en lugar de declararse con la sintaxis de función habitual. No tiene nombre, pero se puede invocar mediante una referencia de función . Estas funciones se pueden definir dentro de una función, así como en otros ámbitos. Para usar variables locales en la función anónima, utilice un cierre .

Alternativas por idioma

Los siguientes lenguajes proporcionan características similares a las funciones anidadas:

  • C++ las clases permiten una ocultación y encapsulación de datos similar; definir una clase dentro de otra clase proporciona una estructura similar (véase el objeto Función en C++ ).
  • C++11 y posteriores : mediante expresiones lambda (véase el ejemplo de quicksort anterior) [ 11 ]
  • Eiffel prohíbe explícitamente el anidamiento de rutinas para mantener el lenguaje simple; permite la convención de usar una variable especial, Result , para denotar el resultado de una función (que devuelve un valor).

Implementación

La implementación de funciones anidadas puede ser más compleja de lo que parece, ya que una referencia a una función anidada que hace referencia a variables no locales crea un cierre . Por esta razón, las funciones anidadas no son compatibles con algunos lenguajes como C, C++ o Java, ya que esto dificulta la implementación por parte de los compiladores. [ 10 ] [ 13 ] Sin embargo, algunos compiladores sí las admiten como una extensión específica del compilador. Un ejemplo conocido es la implementación de C de GNU C, que comparte código con compiladores para lenguajes como Pascal, Ada y Modula.

Acceso a objetos no locales

Existen varias formas de implementar procedimientos anidados en un lenguaje con ámbito léxico, pero la forma clásica es la siguiente:

Cualquier objeto no local , X, se alcanza mediante enlaces de acceso en los marcos de activación de la pila de la máquina. El llamador, C, ayuda al procedimiento llamado, P, insertando un enlace directo a la última activación de la encapsulación léxica inmediata de P, (P), antes de la llamada. De esta forma, P puede encontrar rápidamente la activación correcta para un X determinado siguiendo un número fijo de enlaces (P.depth – X.depth), que normalmente es pequeño.
El emisor crea este enlace directo siguiendo (por sí mismo) los enlaces más antiguos C.depth – P.depth + 1, que conducen a la última activación de (P), y luego uniendo temporalmente estos con un enlace directo a esa activación; el enlace desaparece posteriormente junto con P, por lo que los enlaces más antiguos que se encuentran debajo pueden volver a utilizarse.
Tenga en cuenta que P es visible para C y, por lo tanto, puede ser llamado por C si (P) = C / (C) / ((C)) / etc.

Este método original es más rápido de lo que parece, pero aun así suele optimizarse en los compiladores modernos (mediante el uso de pantallas o técnicas similares).

Otra forma de implementar funciones anidadas que utilizan algunos compiladores es convertir ("elevar") funciones anidadas en funciones no anidadas (donde los parámetros adicionales y ocultos reemplazan los enlaces de acceso) mediante un proceso conocido como elevación lambda durante una etapa intermedia de la compilación.

Funciona como valores

Para que las funciones locales con no locales de ámbito léxico se pasen como resultados, el código de tiempo de ejecución del lenguaje también debe pasar implícitamente el entorno (datos) que la función ve dentro de su función contenedora, de modo que sea accesible incluso cuando la activación actual de la función contenedora ya no exista. [ 14 ] Esto significa que el entorno debe almacenarse en un área de memoria diferente a (las partes posteriormente recuperadas de) una pila de ejecución basada en cronología, lo que, a su vez, implica algún tipo de asignación de memoria libremente dinámica . Muchos lenguajes antiguos basados ​​en Algol (o dialectos de los mismos) no permiten que las funciones locales que acceden a no locales se pasen como valores de retorno, o no permiten funciones como valores de retorno en absoluto, aunque pasar dichas funciones como argumentos aún puede ser posible.

Pilas sin ejecución

La implementación de funciones anidadas de GCC en C provoca la pérdida de pilas de no ejecución (pilas NX). Por lo tanto, el software diseñado con Secure Development Lifecycle a menudo no permite el uso de las funciones anidadas de GCC. [ 15 ] Por defecto, GCC emitirá una breve notificación cuando un archivo objeto generado requiera una pila ejecutable. Para una advertencia clara al compilar código C, utilice la opción.Wtrampolines

El problema se produce porque GCC utiliza "trampolines" (código ejecutable en la pila) para saltar a funciones anidadas. [ 16 ] El proyecto GCC tiene un método alternativo que podría proporcionar funciones anidadas a través de descriptores no ejecutables; sin embargo, actualmente solo se utiliza en el compilador Ada de GCC .

Véase también

Referencias

  1. 1 2 Brillante 2004 .
  2. Funciones de orden superior y expresiones lambda - Lenguaje de programación Kotlin
  3. Rothwell, Trevis J. (2011). El manual de referencia de GNU C. Free Software Foundation, Inc. pág.  63.
  4. Re: Anidamiento de funciones: ¿Por qué?, baavgai ,14 de enero de 2012
  5. "Un recorrido por el lenguaje de Dart" .
  6. "Funciones | Kotlin" .
  7. "Métodos anidados" .
  8. "Funciones anidadas: uso de la colección de compiladores GNU (GCC)" . Proyecto GNU . Consultado el 6 de enero de 2007 .
  9. "Un recorrido por Go" .
  10. 1 2 3 " Pregunta 20.24: ¿Por qué C no tiene funciones anidadas? , preguntas frecuentes de comp.lang.c
  11. "Función anidada - Código Rosetta" .
  12. "Función anidada - Código Rosetta" .
  13. Respuesta de Dave Vandervies, 28 de agosto de 2009 a las 17:45, a "¿ Por qué el estándar C no admite funciones anidadas? "
  14. Dicha combinación de código de función y su entorno se denomina a veces clausura .
  15. Walton, Jeffrey. "Fortalecimiento de la cadena de herramientas basada en C" . The Open Web Application Security Project (OWASP) . Consultado el 28 de febrero de 2017 .
  16. "Trampolines" . Documentación interna de GCC . GNU . Consultado el 12 de mayo de 2026 .
  • Bright, Walter (1 de mayo de 2004). "Funciones anidadas" . Dr. Dobb's .
  • Preguntas frecuentes sobre comp.lang.c: Funciones anidadas
  • "6.4 Procedimientos y funciones anidadas" . Documentación de FreePascal.