En la programación de computadoras , una declaración de retorno hace que la ejecución abandone la subrutina actual y se reanude en el punto del código inmediatamente después de la instrucción que llamó a la subrutina, conocida como su dirección de retorno . La dirección de retorno es guardada por la rutina de llamada, hoy por lo general en la pila de llamadas del proceso o en un registro . Las declaraciones de retorno en muchos lenguajes permiten que una función especifique un valor de retorno que se devolverá al código que llamó a la función.
Descripción general
En C y C ++ , (donde es una expresión ) es una declaración que le dice a una función que devuelva la ejecución del programa a la función que llama y que informe el valor de . Si una función tiene el tipo de retorno void , la declaración de retorno se puede usar sin un valor, en cuyo caso el programa simplemente sale de la función actual y regresa a la que llama.return exp;
exp
exp
En Pascal no hay declaración de devolución. (Sin embargo, en los Pascal más nuevos, se puede usar para devolver un valor inmediatamente. Sin parámetros, simplemente rompe el procedimiento). Una subrutina regresa automáticamente cuando la ejecución alcanza su última instrucción ejecutable. Los valores se pueden devolver asignando a un identificador que tiene el mismo nombre que la subrutina, una función en la terminología de Pascal. De esta forma, el identificador de función se utiliza para llamadas recursivas y como portador de resultados; esto es sintácticamente similar a un parámetro de salida explícito . La misma sintaxis se usa en Fortran 66 y Fortran 77, aunque se agregó una declaración de retorno en FORTRAN II . En algunos otros lenguajes, se utiliza una variable de resultado definida por el usuario en lugar del identificador de función.Exit(exp);
Oberon ( Oberon-07 ) tiene una cláusula de devolución en lugar de una declaración de devolución. La cláusula de devolución se coloca después de la última declaración del cuerpo del procedimiento. Esto permite la verificación en tiempo de compilación del valor devuelto y devuelto adecuado del procedimiento.
Algunos lenguajes de programación orientados a expresiones , como Lisp , Perl y Ruby , permiten al programador omitir una declaración de retorno explícita, especificando en cambio que la última expresión evaluada es el valor de retorno de la subrutina.
En otros casos, se devuelve un valor nulo si no hay una declaración de devolución explícita: en Python , el valor None
se devuelve cuando se omite la declaración de devolución, mientras que en JavaScript undefined
se devuelve el valor .
En Windows PowerShell, todas las expresiones evaluadas que no se capturan (por ejemplo, asignadas a una variable, convertidas a vacío o canalizadas a $ null ) se devuelven desde la subrutina como elementos en una matriz, o como un solo objeto en el caso de que solo un objeto no ha sido capturado.
En Perl, un valor o valores de retorno de una subrutina pueden depender del contexto en el que se llamó. La distinción más fundamental es un contexto escalar donde el código de llamada espera un valor, un contexto de lista donde el código de llamada espera una lista de valores y un contexto vacío donde el código de llamada no espera ningún valor de retorno. Una subrutina puede verificar el contexto usando la wantarray
función. Se utiliza una sintaxis especial de retorno sin argumentos para devolver un valor indefinido en contexto escalar y una lista vacía en contexto de lista. El contexto escalar se puede dividir en contextos booleanos , numéricos, de cadena y de varios tipos de referencia . Además, un objeto sensible al contexto se puede devolver utilizando una secuencia de retorno contextual, con una evaluación perezosa de los valores escalares.
Muchos sistemas operativos permiten que un programa devuelva un resultado (separado de la salida normal ) cuando termina su proceso; estos valores se denominan códigos de retorno o, más específicamente, estados de salida . La cantidad de información que se puede transmitir de esta manera es bastante limitada, en la práctica a menudo se limita a señalar el éxito o el fracaso. Desde dentro del programa, este retorno se logra típicamente llamando a Exit (llamada al sistema) (común incluso en C, donde está disponible el mecanismo alternativo de retorno desde la función principal ).
Sintaxis
Las declaraciones de devolución tienen muchas formas. Las siguientes sintaxis son las más comunes:
Idioma | Declaración de devolución | Si se omite el valor, devuelve |
---|---|---|
Ada , Bourne shell , [1] C , C ++ , Java , PHP , C # , JavaScript , D | valor de retorno ; | en el shell Bourne, valor de salida del último comando ejecutado en la función en C [2] y C ++, [3] comportamiento indefinido si la función devuelve valor en PHP, [4] devuelve en JavaScript, [5] devuelve el valorundefined en Java y C #, no permitido si la función devuelve valor |
BÁSICO | REGRESO | |
Ceceo | ( valor de retorno ) | último valor de declaración |
Perl , Ruby | return @values ; return $ valor ; volver ; o una secuencia de retorno contextual | último valor de declaración |
PL / I | return (expresión);regreso; | comportamiento indefinido si se declara que el procedimiento devuelve un valor |
Pitón | valor de retorno | None |
Charla | ^ valor | |
Tcl | return return $ value return - código de error "Mensaje de error" o alguna combinación de opciones más complicada | último valor de declaración |
Visual Basic .NET | Valor devuelto | |
Windows PowerShell | valor de retorno ; | objeto |
montaje x86 | retirado | contenido del registro eax (por convenciones) |
En algunos lenguajes ensambladores , por ejemplo, para MOS Technology 6502 , se utiliza el mnemónico "RTS" (ReTurn from Subroutine).
Declaraciones de devolución múltiples
Los lenguajes con una declaración de devolución explícita crean la posibilidad de múltiples declaraciones de devolución en la misma función. Si eso es algo bueno o no es controvertido.
Los partidarios firmes de la programación estructurada se aseguran de que cada función tenga una sola entrada y una sola salida (SESE). Por lo tanto, se ha argumentado [6] que uno debe evitar el uso de la declaración de retorno explícita excepto en el extremo textual de una subrutina, considerando que, cuando se usa para "regresar temprano", puede sufrir el mismo tipo de problemas. que surgen para la declaración GOTO . Por el contrario, se puede argumentar que vale la pena usar la declaración de retorno cuando la alternativa es un código más complicado, como un anidamiento más profundo, perjudicando la legibilidad.
En su libro de texto de 2004, David Watt escribe que "los flujos de control de entrada múltiple y salida única son a menudo deseables". Utilizando la noción de secuenciador del marco de Tennent , Watt describe uniformemente las construcciones de flujo de control que se encuentran en los lenguajes de programación contemporáneos e intenta explicar por qué ciertos tipos de secuenciadores son preferibles a otros en el contexto de los flujos de control de múltiples salidas. Watt escribe que los gotos sin restricciones (secuenciadores de salto) son malos porque el destino del salto no se explica por sí mismo para el lector de un programa hasta que el lector encuentra y examina la etiqueta o dirección real que es el objetivo del salto. Por el contrario, Watt sostiene que la intención conceptual de un secuenciador de retorno se desprende de su propio contexto, sin tener que examinar su destino. Además, Watt escribe que una clase de secuenciadores conocidos como secuenciadores de escape , definidos como "secuenciadores que terminan la ejecución de un comando o procedimiento que los encierra textualmente", abarca tanto las rupturas de los bucles (incluidas las rupturas de varios niveles) como las declaraciones de retorno. Watt también señala que, si bien los secuenciadores de salto (gotos) se han restringido un poco en lenguajes como C, donde el objetivo debe ser un bloque interno o un bloque externo envolvente, esa restricción por sí sola no es suficiente para hacer que la intención de los gotos en C sea propia. -describiendo y así todavía pueden producir " código espagueti ". Watt también examina en qué se diferencian los secuenciadores de excepción de los secuenciadores de escape y salto; para obtener detalles sobre esto, consulte el artículo sobre programación estructurada . [7]
Según estudios empíricos citados por Eric S. Roberts , los estudiantes programadores tenían dificultades para formular soluciones correctas para varios problemas simples en un lenguaje como Pascal , que no permite múltiples puntos de salida. Para el problema de escribir una función para buscar linealmente un elemento en una matriz, un estudio de 1980 de Henry Shapiro (citado por Roberts) encontró que usando solo las estructuras de control proporcionadas por Pascal, la solución correcta fue dada por solo el 20% de los sujetos , mientras que ningún sujeto escribió un código incorrecto para este problema si se le permitía escribir un retorno desde el medio de un bucle. [8]
Otros, incluidos Kent Beck y Martin Fowler, argumentan que una o más cláusulas de protección (declaraciones de retorno condicional de "salida anticipada" cerca del comienzo de una función) a menudo hacen que una función sea más fácil de leer que la alternativa. [9] [10] [11] [12]
El problema más común en la salida anticipada es que la limpieza o las declaraciones finales no se ejecutan; por ejemplo, la memoria asignada no está sin asignar o los archivos abiertos no se cierran, lo que provoca fugas. Estos deben realizarse en cada sitio de devolución, que es frágil y puede provocar fácilmente errores. Por ejemplo, en un desarrollo posterior, un desarrollador podría pasar por alto una declaración de retorno, y una acción que debería realizarse al final de una subrutina (por ejemplo, una declaración de seguimiento ) podría no realizarse en todos los casos. Los idiomas sin una declaración de devolución, como el Pascal estándar , no tienen este problema. Algunos lenguajes, como C ++ y Python, emplean conceptos que permiten que las acciones se realicen automáticamente al regresar (o lanzar una excepción), lo que mitiga algunos de estos problemas, que a menudo se conocen como "probar / finalmente" o similares. Funcionalidades como estas cláusulas "finalmente" se pueden implementar mediante un goto al punto de retorno único de la subrutina. Una solución alternativa es usar el desenrollado normal de la pila (desasignación de variables) en la salida de la función para desasignar recursos, como mediante destructores en variables locales, o mecanismos similares como la declaración "with" de Python.
Algunas implementaciones tempranas de lenguajes como el Pascal original y C restringieron los tipos que pueden ser devueltos por una función (por ejemplo, no admitir tipos de registros o estructuras ) para simplificar sus compiladores .
En Java, y en lenguajes similares modelados a partir de él, como JavaScript , es posible ejecutar código incluso después de la declaración return, porque el bloque finalmente de una estructura try-catch siempre se ejecuta. Entonces, si la declaración de retorno se coloca en algún lugar dentro de los bloques try o catch , el código finalmente (si se agrega) se ejecutará. Incluso es posible alterar el valor de retorno de un tipo no primitivo (una propiedad de un objeto ya devuelto) porque la salida también ocurre después. [13]
Declaraciones de rendimiento
Primo a las declaraciones de regreso son declaraciones de rendimiento : cuando un retorno causa una sub rutina para terminar, un rendimiento provoca un co rutina a suspender. La corrutina continuará más tarde desde donde se suspendió si se vuelve a llamar. Las corrutinas son significativamente más complicadas de implementar que las subrutinas y, por lo tanto, las declaraciones de rendimiento son menos comunes que las declaraciones de retorno, pero se encuentran en varios lenguajes.
Secuencias de llamada / devolución
Es posible una serie de posibles secuencias de llamada / devolución según el conjunto de instrucciones del hardware, incluidas las siguientes:
- La
CALL
instrucción empuja la dirección de la siguiente instrucción en la pila y se bifurca a la dirección especificada. LaRETURN
instrucción saca la dirección de retorno de la pila al puntero de instrucción y la ejecución se reanuda en esa dirección. (Ejemplos x86, PDP-11) - La
CALL
instrucción coloca la dirección de la siguiente instrucción en un registro y se bifurca a la dirección especificada. LaRETURN
secuencia de instrucciones coloca la dirección de retorno del registro en el puntero de instrucción y la ejecución se reanuda en esa dirección. (Ejemplo IBM System / 360) - La
CALL
instrucción coloca la dirección de la instrucción siguiente (o actual ) en la ubicación de almacenamiento en la dirección de llamada y se ramifica a la dirección especificada + 1. LaRETURN
secuencia de instrucciones se bifurca a la dirección de retorno mediante un salto indirecto a la primera instrucción de la subrutina. (Ejemplos IBM 1130, SDS9XX)
Ver también
- Tipo de retorno
- Estado de salida
Referencias
- ^ en el shell de Bourne, solo se pueden devolver enteros en el rango 0-255: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#return
- ^ https://msdn.microsoft.com/en-us/library/sta56yeb.aspx MSDN: declaración de devolución (C)
- ^ https://msdn.microsoft.com/en-us/library/k68ktdwf.aspx MSDN: declaración de retorno (C ++)
- ^ "PHP: retorno - Manual" . Manual de PHP . El Grupo PHP . Consultado el 26 de marzo de 2013 .
- ^ "Retorno - Javascript" . Referencia de MDN Javascript . Red de desarrolladores de Mozilla . Consultado el 27 de marzo de 2013 .
- ^ Notas de C ++: instrucción de retorno de función
- ^ David Anthony Watt; William Findlay (2004). Conceptos de diseño de lenguajes de programación . John Wiley e hijos. págs. 215-221. ISBN 978-0-470-85320-7.
- ^ Roberts, E. [1995] "Salidas de bucle y programación estructurada: reapertura del debate", Boletín ACM SIGCSE, (27) 1: 268-272.
- ^ Martin Fowler, Kent Beck, John Brant, William Opdyke, Don Roberts. "Refactorización: mejora del diseño de código existente (Google eBook)" . sección "Reemplazo de cláusulas condicionales anidadas con protecciones". 2012. p. 237, pág. 250. cita: "... mentalidad de un punto de salida ... No sigo la regla sobre un punto de salida de un método".
- ^ Kent Beck. "Patrones de implementación" . 2007. "Capítulo 7: Comportamiento", apartado "Cláusula de protección".
- ^ "Declaraciones de devolución múltiple"
- ^ Fred Swartz. "Declaraciones de retorno y la fantasía de salida única" .
- ^ El bloque finalmente, los tutoriales de Java