Incluso los lenguajes donde tiene una manipulación explícita del puntero como C siempre se pasa por valor ( puede pasarlos por referencia, pero ese no es el comportamiento predeterminado).
¿Cuál es el beneficio de esto? ¿Por qué se pasan tantos idiomas por valores y por qué otros se pasan por referencia ? (Entiendo que Haskell se pasa por referencia, aunque no estoy seguro).
programming-languages
language-design
Mi código no tiene errores
fuente
fuente
void acceptEntireProgrammingLanguageByValue(C++);
temp := a; a := b; b := temp;
y cuando regrese, los valores dea
yb
serán intercambiados. No hay forma de hacer eso en C; tienes que pasar punteros aa
yb
y laswap
rutina debe actuar sobre los valores a los que apuntan.Respuestas:
Pasar por valor es a menudo más seguro que pasar por referencia, porque no puede modificar accidentalmente los parámetros a su método / función. Esto hace que el lenguaje sea más simple de usar, ya que no tiene que preocuparse por las variables que le da a una función. Sabes que no se cambiarán, y esto es a menudo lo que esperas .
Sin embargo, si desea modificar los parámetros, necesita tener alguna operación explícita para aclarar esto (pasar un puntero). Esto obligará a todas las personas que llaman a hacer la llamada de manera ligeramente diferente (
&variable
, en C) y esto hace explícito que el parámetro variable puede cambiarse.Entonces, ahora puede suponer que una función no cambiará su parámetro variable, a menos que esté marcada explícitamente para hacerlo (al requerirle que pase un puntero). Esta es una solución más segura y limpia que la alternativa: suponga que todo puede cambiar sus parámetros, a menos que ellos digan específicamente que no pueden.
fuente
La llamada por valor y la llamada por referencia son técnicas de implementación que se confundieron con los modos de paso de parámetros hace mucho tiempo.
Al principio, estaba FORTRAN. FORTRAN solo tenía llamada por referencia, ya que las subrutinas tenían que poder modificar sus parámetros, y los ciclos de computación eran demasiado caros para permitir múltiples modos de paso de parámetros, además no se sabía lo suficiente sobre la programación cuando se definió por primera vez FORTRAN.
A ALGOL se le ocurrió llamar por nombre y llamar por valor. La llamada por valor era para cosas que no debían cambiarse (parámetros de entrada). La llamada por nombre era para los parámetros de salida. Call-by-name resultó ser un gran problema, y ALGOL 68 lo dejó caer.
PASCAL proporcionó llamada por valor y llamada por referencia. No proporcionó ninguna forma para que el programador le dijera al compilador que estaba pasando un objeto grande (generalmente una matriz) por referencia, para evitar volar la pila de parámetros, pero que el objeto no debería cambiarse.
PASCAL agregó punteros al léxico del diseño del lenguaje.
C proporcionó una llamada por valor y una llamada por referencia simulada definiendo un operador kludge para devolver un puntero a un objeto arbitrario en la memoria.
Los lenguajes posteriores copiaron C, principalmente porque los diseñadores nunca habían visto nada más. Esta es probablemente la razón por la cual la llamada por valor es tan popular.
C ++ agregó un kludge encima del C kludge para proporcionar una llamada por referencia.
Ahora, como resultado directo de call-by-value vs. call-by-reference vs. call-by-pointer-kludge, C y C ++ (programadores) tienen dolores de cabeza horribles con punteros constantes y punteros const (solo lectura) objetos.
Ada logró evitar toda esta pesadilla.
Ada no tiene una llamada explícita por valor frente a una llamada por referencia. Por el contrario, Ada tiene parámetros de entrada (que pueden leerse pero no escritos), parámetros de salida (que DEBEN escribirse antes de que puedan leerse) y parámetros de salida, que pueden leerse y escribirse en cualquier orden. El compilador decide si un parámetro en particular se pasa por valor o por referencia: es transparente para el programador.
fuente
In the beginning, there was FORTRAN
.0
prefijo es inconsistente con cualquier otro prefijo no base diez. Eso puede ser algo excusable en C (y en su mayoría lenguajes compatibles con la fuente, por ejemplo, C ++, Objective C), si octal era la única base alternativa cuando se introdujo, pero cuando los lenguajes más modernos usan ambos0x
y0
desde el principio, solo los hace verse mal pensadoEl pase por referencia permite efectos secundarios no intencionales muy sutiles que son muy difíciles de rastrear cuando comienzan a causar el comportamiento no deseado.
El paso por valor, especialmente
final
,static
oconst
los parámetros de entrada hacen que toda esta clase de errores desaparezcan.Los lenguajes inmutables son aún más deterministas y más fáciles de razonar y comprender qué está entrando y qué se espera que salga de una función.
fuente
Swap
hacks que no usan una variable temporal, aunque probablemente no sean un problema en sí mismos, muestran lo difícil que puede ser depurar un ejemplo más complejo.El punto de dividir los programas grandes en pequeñas subrutinas es que puede razonar sobre las subrutinas de forma independiente. El paso por referencia rompe esta propiedad. (Al igual que el estado mutable compartido).
En realidad, C siempre es paso por valor, nunca paso por referencia. Puede tomar una dirección de algo y pasar esa dirección, pero la dirección aún se pasará por valor.
Hay dos razones principales para usar el paso por referencia:
Personalmente, creo que el n. ° 1 es falso: casi siempre es una excusa para una mala API y / o diseño de lenguaje:
También puede simular múltiples valores de retorno al empaquetarlos en una estructura de datos liviana, como una tupla. Esto funciona particularmente bien si el lenguaje admite la coincidencia de patrones o el enlace de desestructuración. Ej. Ruby:
A menudo, ni siquiera necesita múltiples valores de retorno. Por ejemplo, un patrón popular es que la subrutina devuelve un código de error y el resultado real se vuelve a escribir por referencia. En su lugar, solo debe lanzar una excepción si el error es inesperado o devolver un
Either<Exception, T>
si se espera el error. Otro patrón es devolver un valor booleano que indica si la operación fue exitosa y devolver el resultado real por referencia. Nuevamente, si la falla es inesperada, debe lanzar una excepción, si se espera la falla, por ejemplo, al buscar un valor en un diccionario, debe devolver unMaybe<T>
lugar.El paso por referencia puede ser más eficiente que el paso por valor, ya que no tiene que copiar el valor.
No, Haskell no es de referencia. Tampoco es paso por valor. Pasar por referencia y pasar por valor son estrategias de evaluación estrictas, pero Haskell no es estricto.
De hecho, la Especificación de Haskell no especifica ninguna estrategia de evaluación en particular. La mayoría de las implementaciones de Hakell usan una combinación de llamada por nombre y llamada por necesidad (una variante de llamada por nombre con memorización), pero el estándar no exige esto.
Tenga en cuenta que incluso para lenguajes estrictos, no tiene sentido distinguir entre pasar por referencia o pasar por valor para lenguajes funcionales, ya que la diferencia entre ellos solo se puede observar si muta la referencia. Por lo tanto, el implementador es libre de elegir entre los dos, sin romper la semántica del lenguaje.
fuente
Hay un comportamiento diferente según el modelo de llamada del lenguaje, el tipo de argumentos y los modelos de memoria del lenguaje.
Con tipos nativos simples, pasar por valor le permite pasar el valor por los registros. Eso puede ser muy rápido ya que el valor no necesita cargarse desde la memoria ni guardarse de nuevo. Una optimización similar también es posible simplemente reutilizando la memoria utilizada por el argumento una vez que la persona que llama ha terminado con él, sin arriesgarse a meterse con la copia del objeto que llama. Si el argumento fuera un objeto temporal, probablemente habría guardado una copia completa al hacerlo (C ++ 11 hace que este tipo de optimización sea aún más obvio con la nueva referencia a la derecha y su movimiento semántico).
En muchos lenguajes OO (C ++ es más una excepción en este contexto), no puede pasar un objeto por valor. Estás obligado a pasarlo por referencia. Esto hace que el código sea polimórfico por defecto y está más en línea con la noción de instancias propias de OO. Además, si desea pasar por valor, debe hacer una copia usted mismo, reconociendo el costo de rendimiento que generó dicha acción. En este caso, el idioma elige para usted el enfoque que es más probable que le brinde el mejor rendimiento.
En el caso de los lenguajes funcionales, supongo que pasar por valor o referencia es simplemente una cuestión de optimización. Dado que las funciones en dicho lenguaje son puras y están libres de efectos secundarios, realmente no hay razón para copiar el valor, excepto la velocidad. Incluso estoy bastante seguro de que dicho lenguaje a menudo comparte la misma copia de objetos con los mismos valores, una posibilidad disponible solo si usó una semántica de referencia (const). Python también usó este truco para enteros y cadenas comunes (como métodos y nombres de clase), explicando por qué los enteros y las cadenas son objetos constantes en Python. Esto también ayuda a optimizar el código nuevamente, permitiendo, por ejemplo, la comparación de punteros en lugar de la comparación de contenido, y haciendo una evaluación diferida de algunos datos internos.
fuente
Puedes pasar una expresión por valor, es natural. Pasar la expresión por referencia (temporal) es ... raro.
fuente
Si pasa por referencia, entonces siempre está trabajando efectivamente con valores globales, con todos los problemas de los globales (es decir, alcance y efectos secundarios no deseados).
Las referencias, al igual que los globales, a veces son beneficiosas, pero no deberían ser su primera opción.
fuente