Como entiendo más o menos el modelo de sustitución (con transparencia referencial (RT)), puede descomponer una función en sus partes más simples. Si la expresión es RT, puede descomponer la expresión y obtener siempre el mismo resultado.
Sí, la intuición es bastante correcta. Aquí hay algunos consejos para ser más precisos:
Como dijiste, cualquier expresión RT debería tener un single"resultado". Es decir, dada una factorial(5)expresión en el programa, siempre debe producir el mismo "resultado". Por lo tanto, si cierto factorial(5)está en el programa y produce 120, siempre debe producir 120 independientemente de qué "orden de pasos" se expanda / calcule, independientemente del tiempo .
Ejemplo: la factorialfunción.
def factorial(n):
if n == 1:
return 1
return n * factorial(n - 1)
Hay algunas consideraciones con esta explicación.
En primer lugar, tenga en cuenta que los diferentes modelos de evaluación (ver el orden aplicativo versus el orden normal) pueden producir diferentes "resultados" para la misma expresión RT.
def first(y, z):
return y
def second(x):
return second(x)
first(2, second(3)) # result depends on eval. model
En el código anterior, firsty secondson referencialmente transparentes, y sin embargo, la expresión al final produce diferentes "resultados" si se evalúa bajo orden normal y orden aplicativo (bajo este último, la expresión no se detiene).
.... lo que lleva al uso de "resultado" entre comillas. Como no se requiere que una expresión se detenga, es posible que no produzca un valor. Así que usar "resultado" es algo borroso. Se puede decir que una expresión RT siempre produce lo mismo computationsbajo un modelo de evaluación.
En tercer lugar, puede ser necesario ver dos que foo(50)aparecen en el programa en diferentes ubicaciones como diferentes expresiones, cada una de las cuales produce sus propios resultados que pueden diferir entre sí. Por ejemplo, si el lenguaje permite un alcance dinámico, ambas expresiones, aunque léxicamente idénticas, son diferentes. En perl:
sub foo {
my $x = shift;
return $x + $y; # y is dynamic scope var
}
sub a {
local $y = 10;
return &foo(50); # expanded to 60
}
sub b {
local $y = 20;
return &foo(50); # expanded to 70
}
El alcance dinámico confunde porque hace que sea fácil pensar xque la única entrada es foo, cuando en realidad, es xy y. Una forma de ver la diferencia es transformar el programa en uno equivalente sin alcance dinámico, es decir, pasar explícitamente los parámetros, por lo que en lugar de definir foo(x), definimos foo(x, y)y pasamos yexplícitamente en las personas que llaman.
El punto es que siempre estamos bajo una functionmentalidad: dada una cierta entrada para una expresión, se nos da un "resultado" correspondiente. Si damos la misma entrada, siempre debemos esperar el mismo "resultado".
Ahora, ¿qué pasa con el siguiente código?
def foo():
global y
y = y + 1
return y
y = 10
foo() # yields 11
foo() # yields 12
El fooprocedimiento rompe RT porque hay redefiniciones. Es decir, definimos yen un punto, y luego, redefinimos eso mismo y . En el ejemplo anterior de perl, los ys son enlaces diferentes aunque comparten el mismo nombre de letra "y". Aquí los ys son en realidad los mismos. Es por eso que decimos que la (re) asignación es una meta operación: de hecho, está cambiando la definición de su programa.
Aproximadamente, las personas generalmente representan la diferencia de la siguiente manera: en un entorno sin efectos secundarios, tiene un mapeo input -> output. En un entorno "imperativo", tiene input -> ouputen el contexto de una stateque puede cambiar con el tiempo.
Ahora, en lugar de simplemente sustituir las expresiones por sus valores correspondientes, uno también tiene que aplicar transformaciones a statecada operación que lo requiera (y, por supuesto, las expresiones pueden consultar eso statepara realizar cálculos).
Entonces, si en un programa libre de efectos secundarios todo lo que necesitamos saber para calcular una expresión es su entrada individual, en un programa imperativo, necesitamos conocer las entradas y el estado completo, para cada paso computacional. El razonamiento es el primero en sufrir un gran golpe (ahora, para depurar un procedimiento problemático, necesita la entrada y el volcado del núcleo). Ciertos trucos se vuelven poco prácticos, como la memorización. Pero también, la concurrencia y el paralelismo se vuelven mucho más desafiantes.
RTimpide usar el.substitution model.El gran problema de no poder usarsubstitution modeles el poder de usarlo para razonar sobre un programa.