Articulo de referencia

Declaración de devolución

En programación , una instrucción `return` provoca que la ejecución abandone la subrutina actual y se reanude en el punto del código inmediatamente posterior a la instrucción qu...

En programación , una instrucción `return` provoca que la ejecución abandone la subrutina actual y se reanude en el punto del código inmediatamente posterior a la instrucción que la llamó, conocido como su dirección de retorno . La rutina que realiza la llamada guarda esta dirección, generalmente en la pila de llamadas del proceso o en un registro . En muchos lenguajes de programación, las instrucciones `return` permiten que una función especifique un valor de retorno que se devolverá al código que la llamó.

Descripción general

En C y C++ , (donde es una expresión ) es una instrucción que le indica a una función que devuelva la ejecución del programa a la función que la llamó e informe el valor de . Si una función tiene el tipo de retorno void , la instrucción return 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 utiliza una sintaxis similar en otros lenguajes, incluidos Modula-2 [ 3 ] y Python . [ 4 ]return exp;expexp

En Pascal no existe la instrucción `return`. Las funciones o procedimientos retornarán automáticamente al llegar a su última instrucción. El valor de retorno de una función se proporciona dentro de la función mediante 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 puede utilizarse para devolver un valor inmediatamente desde una función o, sin parámetros, para retornar 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 instrucción return; dicha instrucció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 otros lenguajes se utiliza un parámetro de salida definido por el usuario en lugar del identificador de la función. [ 9 ]

Oberon ( Oberon-07 ) tiene una cláusula return en lugar de una instrucción return. La cláusula return se coloca después de la última instrucción del cuerpo del procedimiento. [ 10 ]

Algunos lenguajes de programación orientados a expresiones , como Lisp , Perl y Ruby , permiten al programador omitir una instrucció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 nulo si no hay una instrucción de retorno explícita: en Python , se devuelve el valor Nonecuando se omite la instrucció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 redirigidas a $null ) se devuelven desde la subrutina como elementos de una matriz, o como un único objeto en el caso de que solo un objeto no se haya capturado.

En Perl, el valor o los valores de retorno de una subrutina pueden depender del contexto en el que se llama. La distinción más fundamental es un contexto escalar, donde el código que llama espera un único valor; un contexto de lista , donde el código que llama espera una lista de valores; y un contexto vacío, donde el código que llama no espera ningún valor de retorno. Una subrutina puede comprobar el contexto mediante la wantarrayfunción. Se utiliza una sintaxis especial de retorno sin argumentos para devolver un valor indefinido en un contexto escalar y una lista vacía en un contexto de lista. El contexto escalar se puede subdividir en contextos booleanos , numéricos, de cadena y de varios tipos de referencia . Además, se puede devolver un objeto sensible al contexto mediante una secuencia de retorno contextual, con evaluación perezosa de valores escalares.

Muchos sistemas operativos permiten que un programa devuelva un resultado (distinto de la salida normal ) al finalizar su proceso; estos valores se denominan estados de salida . La cantidad de información que se puede transmitir de esta forma es bastante limitada, y en la práctica suele restringirse a indicar si la operación fue exitosa o fallida. Desde dentro del programa, esta devolución se suele lograr mediante la llamada a la función Exit (llamada al sistema) (algo común incluso en C, donde existe el mecanismo alternativo de retorno desde la función principal ).

Sintaxis

Las instrucciones de retorno se presentan en muchas formas. Las siguientes sintaxis son las más comunes:

En algunos lenguajes ensamblador , por ejemplo el del MOS Technology 6502 , se utiliza el mnemónico "RTS" (Return from Subroutine, retorno de subrutina).

Múltiples declaraciones de retorno

Los lenguajes con una instrucción de retorno explícita crean la posibilidad de múltiples instrucciones de retorno en la misma función. Si esto es algo positivo o no, es un tema controvertido.

Los defensores 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 debe evitar el uso de la instrucción `return` explícita, excepto al final del texto de una subrutina, ya que, al utilizarse para "retornar anticipadamente", puede presentar los mismos problemas que la instrucción `GOTO` . Por el contrario, se puede argumentar que el uso de la instrucción `return` es útil cuando la alternativa implica un código más complejo, como un anidamiento más profundo, lo 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 el concepto de secuenciador del marco de Tennent , Watt describe uniformemente las estructuras de flujo de control presentes 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 salida múltiple. Watt escribe que los gotos sin restricciones (secuenciadores de salto) son malos porque el destino del salto no es autoexplicativo para el lector de un programa hasta que este encuentra y examina la etiqueta o dirección real que es el destino 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 necesidad de 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 contiene textualmente", abarca tanto las interrupciones de bucles (incluidas las interrupciones multinivel) como las sentencias de retorno. Watt también señala que, si bien los secuenciadores de salto (gotos) han estado algo restringidos en lenguajes como C, donde el destino debe estar dentro del bloque local o en 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 pueden producir " código espagueti ". Watt también examina cómo los secuenciadores de excepciones difieren de los secuenciadores de escape y de salto; para 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 sencillos 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 un array, un estudio de 1980 realizado por Henry Shapiro (citado por Roberts) encontró que, utilizando únicamente las estructuras de control proporcionadas por Pascal, la solución correcta fue dada solo por el 20% de los sujetos, mientras que ningún sujeto escribió código incorrecto para este problema si se le permitía escribir un retorno desde el medio de un bucle. [ 16 ]

Otros, como Kent Beck y Martin Fowler, argumentan que una o más cláusulas de guarda —declaraciones de retorno condicionales de "salida temprana" cerca del inicio 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 las salidas anticipadas es que las instrucciones de limpieza o finales no se ejecutan; por ejemplo, la memoria asignada no se libera o los archivos abiertos no se cierran, lo que provoca fugas de memoria. Estas acciones deben realizarse en cada punto de retorno, lo que resulta frágil y puede generar errores fácilmente. Por ejemplo, en el desarrollo posterior, un desarrollador podría pasar por alto una instrucción `return`, y una acción que debería ejecutarse al final de una subrutina (como una instrucción de rastreo ) podría no ejecutarse en todos los casos. Los lenguajes sin instrucción `return`, como el estándar Pascal, no presentan este problema. Algunos lenguajes, como C++ y Python, emplean conceptos que permiten que las acciones se ejecuten automáticamente al regresar (o al lanzar una excepción), lo que mitiga algunos de estos problemas; estos conceptos se conocen comúnmente como `try/finally` o similares. Funcionalidades como estas cláusulas `finally` pueden implementarse mediante una instrucción `goto` al único punto de retorno de la subrutina. Una solución alternativa consiste en utilizar el desenrollado normal de la pila (desasignación de variables) al salir de la función para liberar recursos, por ejemplo, mediante destructores en variables locales o mecanismos similares como la instrucción "with" de Python.

Algunas implementaciones tempranas de lenguajes como el Pascal original y C restringieron los tipos que podía devolver una función (por ejemplo, no admitían tipos de registro o estructura ) para simplificar sus compiladores .

En Java —y lenguajes similares inspirados en él, como JavaScript— es posible ejecutar código incluso después de una instrucción `return`, ya que el bloque `finally` de una estructura `try-catch` siempre se ejecuta. Por lo tanto, si la instrucción `return` se coloca dentro de bloques `try` o `catch`, el código dentro de `finally` (si se agregó) se ejecutará. Incluso es posible modificar el valor de retorno de un tipo no primitivo (una propiedad de un objeto ya devuelto) porque la salida también ocurre posteriormente. [ 21 ]

Declaraciones de rendimiento

Las instrucciones yield son similares a las instrucciones return : mientras que return provoca la finalización de una subrutina , yield provoca la suspensión de una corrutina . Si se vuelve a llamar a la corrutina , esta continuará su ejecución desde donde se suspendió. Las corrutinas son mucho más complejas de implementar que las subrutinas, por lo que las instrucciones yield son menos comunes que las instrucciones return, aunque se encuentran en varios lenguajes de programación.

Secuencias de llamada/retorno

Dependiendo del conjunto de instrucciones del hardware, son posibles varias secuencias de llamada/retorno, como por ejemplo las siguientes:

  1. La CALLinstrucción coloca la dirección de la siguiente instrucción en la pila y salta a la dirección especificada. La RETURNinstrucció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 conocido como 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 ]
  2. La CALLinstrucción coloca la dirección de la siguiente instrucción en un registro y salta a la dirección especificada. La RETURNsecuencia 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 ).
  3. La CALLinstrucción coloca la dirección de la siguiente (o actual ) instrucción en la ubicación de almacenamiento en la dirección de llamada y salta a la dirección especificada + 1. La RETURNsecuencia de instrucciones salta 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 )

Véase también

Notas

  1. en el shell Bourne, solo se pueden devolver enteros en el rango 0–255 [ 11 ]

Referencias

  1. 1 2 "Instrucción return (C)" . Microsoft Docs . 25 de enero de 2023.
  2. 1 2 "Instrucción return (C++)" . Documentación de Microsoft . 3 de agosto de 2021.
  3. Gleaves, R. (2012). Modula-2 para programadores de Pascal . Springer. pág. 71. ISBN  9781461385318.
  4. 1 2 3 Martelli, Alex (2006). Python en pocas palabras: una guía de referencia rápida para escritorio (2.ª ed.). O'Reilly Media. pág. 73. ISBN   9781449379100.
  5. 1 2 Scott, Michael L. (2006). Pragmática del lenguaje de programación . Morgan Kaufmann. pág. 432. ISBN  9780126339512.
  6. Flanders, Harley (2012). Pascal científico . Springer. pág. 35. ISBN  9781461224280.
  7. ANSI x3.9-1966. Norma estadounidense FORTRAN (PDF) . Instituto Nacional Estadounidense de Estándares. pág. 14. Archivado del original (PDF) el 15 de mayo de 2011. Recuperado el 5 de mayo de 2010 . {{cite book}}: CS1 maint: nombres numéricos: lista de autores ( enlace )
  8. ANSI x3.9-1978. Estándar Nacional Estadounidense – Lenguaje de Programación FORTRAN . Instituto Nacional Estadounidense de Estándares. 15.8 Instrucción RETURN. Archivado del original el 29 de octubre de 2013. Recuperado el 11 de diciembre de 2007 .{{cite book}}: CS1 maint: nombres numéricos: lista de autores ( enlace )
  9. Sakkinen, Markku (marzo de 1989). "Cómo devolver mejor el valor de una función" . ACM SIGPLAN Notices . 24 (3). Association for Computing Machinery: 55– 56. doi : 10.1145/66083.66087 .
  10. Wirth, Niklaus (3 de mayo de 2016). "10. Declaraciones de procedimientos". The Programming Language Oberon (PDF) (Informe). pág. 11. 
  11. "return - retorno de una función o script de punto" . Especificación UNIX única .
  12. "PHP: return - Manual" . Manual de PHP . The PHP Group . Consultado el 26 de marzo de 2013 .
  13. "Return - Javascript" . Referencia de Javascript de MDN . Red de desarrolladores de Mozilla . Consultado el 27 de marzo de 2013 .
  14. Swartz, Fred. "Notas de C++: Instrucción de retorno de función" . Archivado del original el 3 de enero de 2007.
  15. Watt, David Anthony; Findlay, William (2004). Conceptos de diseño de lenguajes de programación . John Wiley & Sons. págs. 215–221 . ISBN  978-0-470-85320-7.
  16. Roberts, E. (marzo de 1995). "Salidas de bucle y programación estructurada: reabriendo el debate" . Boletín ACM SIGCSE . 27 (1): 268– 272. doi : 10.1145/199691.199815 .
  17. Martin Fowler; Kent Beck; John Brant; William Opdyke; Don Roberts (2012). Refactoring: Improving the Design of Existing Code (Google eBook) . Addison-Wesley. pp. 237, 250. ISBN  9780133065268. ... mentalidad de un solo punto de salida ... No sigo la regla de un solo punto de salida de un método.
  18. Kent Beck (2007). "7: Comportamiento". Patrones de implementación . Pearson Education. Sección "Cláusula de protección". ISBN 9780132702553.
  19. "Múltiples instrucciones return" . Prácticas de Java .
  20. Fred Swartz. "Instrucciones de retorno y la fantasía de la salida única" . Archivado del original el 23 de febrero de 2020.
  21. "El bloque finally" . Los tutoriales de Java .
  22. ↑ "Manual del usuario del procesador de señal digital DSP96002 de 32 bits" (PDF) . págs. 3-4 . Consultado el 24/12/2023 . 
  23. ↑ "Manual del usuario del procesador de señal digital DSP96002 de 32 bits" (PDF) . págs. 4-11 . Consultado el 24/12/2023 . 
  24. "Procesador de señal digital μPD77C20A, 7720A, 77P20" . pág. 3a - 4 . Consultado el 25/12/2023 .