En programación informática , una sentencia de retorno hace que la ejecución abandone la subrutina actual y se reanude en el punto del código inmediatamente posterior a la instrucción que llamó a la subrutina, conocido como su dirección de retorno . La rutina que llama guarda la dirección de retorno, actualmente por lo general en la pila de llamadas del proceso o en un registro . Las sentencias de retorno en muchos lenguajes de programación permiten que una función especifique un valor de retorno que se pasará de vuelta al código que llamó a la función.
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 la llamó 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 la llamó. [1] [2] Se usa una sintaxis similar en otros lenguajes, incluidos Modula-2 [3] y Python . [4]return exp;
exp
exp
En Pascal no existe una sentencia de retorno. Las funciones o procedimientos retornan automáticamente al llegar a su última sentencia. El valor de retorno de una función se proporciona dentro de la función haciendo una asignación a un identificador con el mismo nombre que la función. [5] Sin embargo, algunas versiones de Pascal proporcionan una función especial que se puede utilizar para devolver un valor inmediatamente desde una función o, sin parámetros, para regresar inmediatamente desde un procedimiento. [6]Exit(exp);
Al igual que Pascal, FORTRAN II , Fortran 66 , Fortran 77 y versiones posteriores de Fortran especifican valores de retorno mediante una asignación al nombre de la función, pero también tienen una declaración de retorno; esa declaración no especifica un valor de retorno y, para una función, hace que se devuelva el valor asignado al nombre de la función. [5] [7] [8]
En algunos otros lenguajes se utiliza un parámetro de salida definido por el usuario en lugar del identificador de función. [9]
Oberon ( Oberon-07 ) tiene una cláusula de retorno en lugar de una declaración de retorno. La cláusula de retorno se coloca después de la última declaración del cuerpo del procedimiento. [10]
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 su lugar que la última expresión evaluada es el valor de retorno de la subrutina. En otros casos, se devuelve un valor Null si no hay una declaración de retorno explícita: en Python , el valor None
se devuelve cuando se omite la declaración de retorno, [4]undefined
mientras que en JavaScript se devuelve el valor .
En Windows PowerShell, todas las expresiones evaluadas que no se capturan (por ejemplo, asignadas a una variable, convertidas a void 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 se haya 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 void donde el código de llamada no espera ningún valor de retorno en absoluto. 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 el contexto escalar y una lista vacía en el contexto de lista. El contexto escalar se puede dividir en contextos booleanos , numéricos, de cadena y de varios tipos de referencia . Además, se puede devolver un objeto sensible al contexto usando una secuencia de retorno contextual, con evaluación diferida de valores escalares.
Muchos sistemas operativos permiten que un programa devuelva un resultado (separado de la salida normal ) cuando su proceso termina; estos valores se denominan estados de salida . La cantidad de información que se puede pasar 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 normalmente llamando a Exit (llamada al sistema) (común incluso en C, donde está disponible el mecanismo alternativo de retorno desde la función principal ).
Las instrucciones de retorno se presentan en muchas formas. Las siguientes sintaxis son las más comunes:
En algunos lenguajes ensambladores , por ejemplo el del MOS Technology 6502 , se utiliza el mnemónico "RTS" (ReTurn from Subroutine).
Los lenguajes con una declaración de retorno explícita crean la posibilidad de múltiples declaraciones de retorno en la misma función. Si esto es bueno o no es un tema controvertido.
Los partidarios acérrimos de la programación estructurada se aseguran de que cada función tenga una única entrada y una única salida (SESE). Por ello, se ha argumentado [14] que se debería evitar el uso de la sentencia return explícita, excepto al final textual de una subrutina, considerando que, cuando se utiliza para "regresar antes", puede sufrir el mismo tipo de problemas que surgen con la sentencia GOTO . Por el contrario, se puede argumentar que vale la pena utilizar la sentencia return cuando la alternativa es un código más complejo, como un anidamiento más profundo, que perjudica la legibilidad.
En su libro de texto de 2004, David Watt escribe que "los flujos de control de entrada única y salida múltiple suelen ser deseables". Utilizando la noción de marco de Tennent de secuenciador , Watt describe de manera uniforme 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 flujos de control de salida múltiple. 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 argumenta que la intención conceptual de un secuenciador de retorno es clara a partir 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 encierra texto", abarcan tanto las interrupciones de bucles (incluidas las interrupciones de varios niveles) como las declaraciones de retorno. Watt también señala que, si bien los secuenciadores de salto (gotos) han sido algo restringidos en lenguajes como C, donde el objetivo debe ser un bloque interno local o un bloque externo que lo abarque, esa restricción por sí sola no es suficiente para que la intención de los gotos en C sea autodescriptiva y, por lo tanto, aún puedan producir " código espagueti ". Watt también examina en qué se diferencian los secuenciadores de excepción de los secuenciadores de escape y de salto; para obtener más detalles sobre esto, consulte el artículo sobre programación estructurada. [15]
Según estudios empíricos citados por Eric S. Roberts , los estudiantes de programación 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 utilizando 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ó código incorrecto para este problema si se le permitió escribir un retorno desde el medio de un bucle. [16]
Otros, incluidos Kent Beck y Martin Fowler, sostienen que una o más cláusulas de protección (declaraciones de retorno condicionales de "salida temprana" cerca del comienzo de una función) a menudo hacen que una función sea más fácil de leer que la alternativa. [17] [18] [19] [20]
El problema más común en la salida temprana es que las instrucciones de limpieza o finales no se ejecutan; por ejemplo, la memoria asignada no se desasigna o los archivos abiertos no se cierran, lo que provoca fugas. Estas operaciones deben realizarse en cada punto de retorno, lo que es frágil y puede generar errores fácilmente. Por ejemplo, en un desarrollo posterior, un desarrollador podría pasar por alto una instrucción de retorno y una acción que debería realizarse al final de una subrutina (por ejemplo, una instrucción de seguimiento ) podría no realizarse en todos los casos. Los lenguajes sin una instrucción de retorno, como 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 al lanzar una excepción), lo que mitiga algunos de estos problemas; a menudo se los conoce como "try/finally" o similares. Una funcionalidad como estas cláusulas "finally" se pueden implementar mediante un goto al único punto de retorno de la subrutina. Una solución alternativa es utilizar el desenrollado de pila normal (desasignación de variables) en la salida de la función para desasignar recursos, como por ejemplo a través de 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 puede devolver una función (por ejemplo, no admitiendo tipos de registro o estructura ) 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 sentencia return, porque el bloque finally de una estructura try-catch siempre se ejecuta. Por lo tanto, si la sentencia return se coloca en algún lugar dentro de los bloques try o catch, el código dentro de finally (si se agrega) se ejecutará. Incluso es posible alterar el valor de retorno de un tipo no primitivo (una propiedad de un objeto ya retornado) porque la salida también ocurre después. [21]
Las instrucciones yield son primas de las instrucciones return : cuando una instrucción return hace que una subrutina finalice , una instrucción yield hace que una co- rutina se suspenda. La corrutina continuará más adelante desde donde se suspendió si se la vuelve a llamar. Las corrutinas son mucho más complejas de implementar que las subrutinas y, por lo tanto, las instrucciones yield son menos comunes que las instrucciones return, pero se encuentran en varios lenguajes.
Son posibles varias secuencias de llamada/retorno según el conjunto de instrucciones del hardware, incluidas las siguientes:
CALL
instrucción introduce la dirección de la siguiente instrucción en la pila y se bifurca a la dirección especificada. La RETURN
instrucción extrae 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 ). En arquitecturas como la Motorola 96000 , el área de la pila puede asignarse en un espacio de direcciones separado, que se denomina "espacio de memoria de pila", [22] distinto del espacio de direcciones de la memoria principal. [23] El NEC μPD7720 también cuenta con una pila con su propio espacio de direcciones separado. [24]CALL
instrucción coloca la dirección de la siguiente instrucción en un registro y se bifurca a la dirección especificada. La RETURN
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. (Ejemplos: IBM System/360 y sucesores hasta z/Architecture , la mayoría de las arquitecturas RISC )CALL
instrucción coloca la dirección de la siguiente instrucción (o actual ) en la ubicación de almacenamiento en la dirección de llamada y se bifurca a la dirección especificada +1. La RETURN
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 , SDS 9XX , PDP-8 ){{cite book}}
: CS1 maint: nombres numéricos: lista de autores ( enlace ){{cite book}}
: CS1 maint: nombres numéricos: lista de autores ( enlace )mentalidad de un solo punto de salida... No sigo la regla de un solo punto de salida de un método.