Pregunta

¿Por qué algunos idiomas diferencian entre métodos que devuelven un valor y métodos que no lo hacen?

es decir en PL / SQL de Oracle, donde la diferencia principal entre una función y un procedimiento es que la función debe devolver un valor, y el procedimiento no debe.

Del mismo modo para los idiomas que no lo hacen, ¿por qué no?


EDITAR: He encontrado una pregunta relacionada que podría interesar a las personas que lean esta pregunta:

¿Fue útil?

Solución

Porque en las concepciones originales de la teoría y práctica de la informática, las funciones y subrutinas prácticamente no tenían nada que ver entre sí.

FORTRAN generalmente se acredita como el primer idioma que implementó ambos y demostró las distinciones. (Early LISP tuvo un papel algo opuesto en esto también, pero tuvo poco impacto fuera de la academia).

Siguiendo las tradiciones de las matemáticas (de las cuales CS todavía era parte en los años 60), las funciones solo se veían como la encapsulación de cálculos matemáticos parametrizados con la única intención de devolver un valor a una expresión más grande. Que podrías llamarlo "desnudo" (F = AZIMUTH (SEGUNDOS)) fue simplemente un caso de uso trivial.

Las subrutinas, por otro lado, fueron vistas como una forma de nombrar un grupo de declaraciones que tenían algún efecto. Los parámetros fueron un gran impulso para su usabilidad y la única razón por la que se les permitió devolver valores de parámetros modificados fue para que pudieran informar su estado sin tener que depender de variables globales.

Entonces, realmente no tenían conexión conceptual, aparte de la encapsulación y los parámetros.

La verdadera pregunta es: "¿Cómo llegaron tantos desarrolladores a verlos como iguales?"

Y la respuesta a eso es C.

Cuando K + R diseñó originalmente su lenguaje de tipo ensamblador de macro de alto nivel para el PDP-11 (¿puede haber comenzado en el PDP-8?), no tenían delirios de independencia de hardware. Prácticamente cada " único " La característica del lenguaje era un reflejo del lenguaje y la arquitectura de la máquina PDP (ver i ++ y --i). Una de ellas fue la comprensión de que las funciones y las subrutinas podían implementarse (y siempre fueron) de manera idéntica en el PDP, excepto que la persona que llama simplemente ignoró el valor de retorno (en R0 [, R1]) para las subrutinas.

Así nació el puntero vacío, y después de que el lenguaje C se había apoderado de todo el mundo de la programación, la percepción errónea de que este artefacto de implementación de HW / OS (aunque cierto en casi todas las plataformas posteriores) fue la misma que la semántica del lenguaje.

Otros consejos

En una configuración pura o de tipo efecto hay un mundo de diferencia, porque obviamente los métodos que "no devuelven nada" solo son útiles por sus efectos secundarios.

Esto es análogo a la distinción entre expresiones y declaraciones, que pueden resolver un lenguaje y eliminar una clase de programas generalmente erróneos (que, por supuesto, es la razón por la que C no lo hace;)).

Para dar un pequeño ejemplo, cuando distingue claramente entre expresiones y declaraciones, if (x = 3) , a diferencia de if (x == 3) es sintácticamente incorrecto (para usar una declaración donde se esperaba una expresión) y no simplemente un error de tipo (para usar un número entero donde se esperaba un booleano). Esto tiene el beneficio de también deshabilitar if (x = true) que sería permitido por una regla basada en tipo en un contexto donde las asignaciones son expresiones que tienen el valor de su operando correcto.

En un lenguaje que encapsula los efectos con mónadas, la distinción importante se convierte en la siguiente:

  • funciones que devuelven () que son funciones puras y solo pueden devolver un valor vacío inútil llamado () o divergen
  • funciones que devuelven IO () (o unidad en alguna otra mónada) que son funciones sin ningún "resultado" excepto los efectos en la mónada IO (o la que sea)

Disculpe respondiendo una pregunta de dos años, especialmente con algo único en mi propio idioma Felix http: // felix-lang. org pero aquí va de todos modos :)

En Felix, las funciones y los procedimientos son fundamentalmente diferentes, y no es solo que los procedimientos tengan efectos secundarios y se invoquen en las declaraciones, mientras que las funciones no tienen efectos secundarios y se usan en expresiones (porque Felix también tiene generadores que son funciones con efectos secundarios .. :)

No, el modelo de ejecución es fundamentalmente diferente, principalmente por razones de rendimiento, pero no del todo. El modelo es:

  • Las funciones ponen su dirección de retorno en la pila de la máquina, y el valor de retorno también.
  • Los procedimientos usan una lista vinculada en el montón. El código de procedimiento es plano, no utiliza la pila de la máquina.

Esto suele ser ineficiente, entonces, ¿por qué hacerlo? La respuesta es: el procedimiento Felix son todas potencialmente co-rutinas (fibras). Pueden cambiar el control a otro procedimiento accediendo a un canal. Esto provoca un intercambio de control.

  • Por razones de rendimiento, copiar la pila de la máquina en el intercambio de control no es una opción.
  • Por razones de administración de memoria, el intercambio de punteros de pila tampoco es una opción.

El sistema operativo normalmente intercambia los punteros de la pila por subprocesos, lo cual es razonablemente rápido, pero tiene un problema fundamental en las máquinas de direcciones lineales: debe limitar el tamaño máximo de la pila a un valor ridículamente pequeño o limitar el número de subprocesos a un valor ridículamente pequeño. En una máquina de 32 bits, no hay suficiente espacio de direcciones para contemplar esta solución. En una máquina de 64 bits, el intercambio de pila tiene más potencial, pero, por supuesto, las demandas de los usuarios siempre superan el hardware 3 días después de su lanzamiento ... :)

Felix simplemente intercambia un solo puntero a las pilas basadas en el montón, por lo que los cambios de contexto son cegadoramente rápidos y se desperdicia muy poco espacio de direcciones. Por supuesto, el costo son las asignaciones del montón en las llamadas a procedimientos.

En el compilador, gran parte de la arquitectura del modelo teórico se optimiza en un "como-si" base, por lo que el rendimiento real y la implementación pueden ser muy diferentes al modelo teórico, siempre que el compilador pueda demostrar que no puede notar la diferencia ... aparte de que se le niegue la oportunidad de tomar una taza de café con tiempo libre:)

Entonces, aquí, tiene una respuesta diferente sobre por qué las funciones y los procedimientos pueden tratarse de manera diferente.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top