stringtranslate.com

Declaración de devolución

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.

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 lo llamó e 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 lo llamó. [1] [2] Se usa una sintaxis similar en otros lenguajes, incluidos Modula-2 [3] y Python . [4]return exp;expexp

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 Nonese 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 wantarrayfunció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 ).

Sintaxis

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).

Declaraciones de retorno múltiples

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 no se ejecutan las instrucciones de limpieza o de finalización; por ejemplo, no se desasigna la memoria asignada o no se cierran los archivos abiertos, 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 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 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]

Declaraciones de rendimiento

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.

Secuencias de llamada/retorno

Son posibles varias secuencias de llamada/retorno según el conjunto de instrucciones del hardware, incluidas las siguientes:

  1. La CALLinstrucción introduce la dirección de la siguiente instrucción en la pila y se bifurca 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, 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]
  2. La CALLinstrucción coloca la dirección de la siguiente instrucción en un registro y se bifurca 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 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 RETURNsecuencia 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 )

Véase también

Notas

  1. ^ En el shell Bourne, solo se pueden devolver números enteros en el rango 0-255 [11]

Referencias

  1. ^ ab "Declaración de retorno (C)". Microsoft Docs . 25 de enero de 2023.
  2. ^ ab "Instrucción de retorno (C++)". Microsoft Docs . 3 de agosto de 2021.
  3. ^ Gleaves, R. (2012). Modula-2 para programadores de Pascal. Springer. pág. 71. ISBN 9781461385318.
  4. ^ abc Martelli, Alex (2006). Python en pocas palabras: una referencia rápida para computadoras de escritorio (2.ª ed.). O'Reilly Media. pág. 73. ISBN 9781449379100.
  5. ^ de 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 desde el original (PDF) el 15 de mayo de 2011. Consultado 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 Declaración RETURN. Archivado desde el original el 29 de octubre de 2013. Consultado 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 obtener el mejor resultado posible del 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". El lenguaje de programación Oberon (PDF) (Informe). pág. 11.
  11. ^ "return - retorno desde una función o script de puntos". Especificación única de UNIX .
  12. ^ "PHP: return - Manual". Manual de PHP . The PHP Group . Consultado el 26 de marzo de 2013 .
  13. ^ "Retorno - Javascript". Referencia de Javascript de MDN . Red de desarrolladores de Mozilla . Consultado el 27 de marzo de 2013 .
  14. ^ "Notas de C++: Declaración de retorno de función".
  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: reapertura del 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). Refactorización: mejora del diseño del código existente (Google eBook). Addison-Wesley. págs. 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 sentencias de retorno". Prácticas de Java .
  20. ^ Fred Swartz. "Declaraciones de retorno y la fantasía de la salida única". Archivado desde el original el 23 de febrero de 2020.
  21. ^ "El bloque finally". Tutoriales de Java .
  22. ^ "MANUAL DEL USUARIO DEL PROCESADOR DE SEÑAL DIGITAL DSP96002 DE 32 BITS" (PDF) . p. 27(3 - ​​4) . Consultado el 24 de diciembre de 2023 .
  23. ^ "MANUAL DEL USUARIO DEL PROCESADOR DE SEÑAL DIGITAL DSP96002 DE 32 BITS" (PDF) . p. 50(4 - 11) . Consultado el 24 de diciembre de 2023 .
  24. ^ "Procesador de señal digital μPD77C20A, 7720A, 77P20". pág. 4(3a-4) . Consultado el 25 de diciembre de 2023 .