Comme je comprends à peu près le modèle de substitution (avec transparence référentielle (RT)), vous pouvez décomposer une fonction dans ses parties les plus simples. Si l'expression est RT, vous pouvez décomposer l'expression et obtenir toujours le même résultat.
Oui, l'intuition a tout à fait raison. Voici quelques conseils pour être plus précis:
Comme vous l'avez dit, toute expression RT doit avoir un single"résultat". Autrement dit, étant donné une factorial(5)expression dans le programme, il devrait toujours donner le même "résultat". Donc, si un certain factorial(5)est dans le programme et qu'il donne 120, il devrait toujours donner 120 quel que soit "l'ordre des étapes", il est développé / calculé - quelle que soit l' heure .
Exemple: la factorialfonction.
def factorial(n):
if n == 1:
return 1
return n * factorial(n - 1)
Il y a quelques considérations avec cette explication.
Tout d'abord, gardez à l'esprit que les différents modèles d'évaluation (voir ordre applicatif vs ordre normal) peuvent donner des "résultats" différents pour la même expression RT.
def first(y, z):
return y
def second(x):
return second(x)
first(2, second(3)) # result depends on eval. model
Dans le code ci-dessus, firstet secondsont référentiellement transparents, et pourtant, l'expression à la fin donne des "résultats" différents si elle est évaluée dans l'ordre normal et l'ordre d'application (sous ce dernier, l'expression ne s'arrête pas).
.... ce qui conduit à l'utilisation de "résultat" entre guillemets. Puisqu'il n'est pas nécessaire qu'une expression s'arrête, elle peut ne pas produire de valeur. Donc, utiliser "résultat" est un peu flou. On peut dire qu'une expression RT donne toujours la même chose computationsdans un modèle d'évaluation.
Troisièmement, il peut être nécessaire de voir deux foo(50)apparaitre dans le programme à des endroits différents comme des expressions différentes - chacune produisant ses propres résultats qui peuvent différer les uns des autres. Par exemple, si le langage autorise une portée dynamique, les deux expressions, bien que lexicalement identiques, sont différentes. 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
}
La portée dynamique induit en erreur car elle permet de penser facilement que xc'est la seule entrée pour foo, alors qu'en réalité, c'est xet y. Une façon de voir la différence est de transformer le programme en programme équivalent sans portée dynamique - c'est-à-dire en passant explicitement les paramètres, donc au lieu de définir foo(x), nous définissons foo(x, y)et passons yexplicitement dans les appelants.
Le fait est que nous sommes toujours dans un functionétat d'esprit: étant donné une certaine entrée pour une expression, nous recevons un "résultat" correspondant. Si nous donnons la même entrée, nous devrions toujours nous attendre au même "résultat".
Maintenant, qu'en est-il du code suivant?
def foo():
global y
y = y + 1
return y
y = 10
foo() # yields 11
foo() # yields 12
La fooprocédure rompt RT car il y a des redéfinitions. Autrement dit, nous avons défini yen un point, et ce dernier sur, redéfini ce même y . Dans l'exemple perl ci-dessus, les ys sont des liaisons différentes bien qu'ils partagent le même nom de lettre "y". Ici, les ys sont en fait les mêmes. C'est pourquoi nous disons que la (ré) affectation est une méta- opération: vous changez en fait la définition de votre programme.
En gros, les gens décrivent généralement la différence comme suit: dans un environnement sans effets secondaires, vous avez une cartographie à partir de input -> output. Dans un cadre «impératif», vous avez input -> ouputdans le cadre d'un statequi peut évoluer dans le temps.
Maintenant, au lieu de simplement substituer des expressions à leurs valeurs correspondantes, il faut également appliquer des transformations stateà chaque opération qui l'exige (et bien sûr, les expressions peuvent les consulter statepour effectuer des calculs).
Donc, si dans un programme libre d'effets secondaires, tout ce que nous devons savoir pour calculer une expression est son entrée individuelle, dans un programme impératif, nous devons connaître les entrées et l'état entier, pour chaque étape de calcul. Le raisonnement est le premier à subir un gros coup (maintenant, pour déboguer une procédure problématique, il faut l'entrée et le core dump). Certaines astuces sont rendues impraticables, comme la mémorisation. Mais aussi, la concurrence et le parallélisme deviennent beaucoup plus difficiles.
RTvous empêche donc d'utiliser le.substitution model.Le gros problème de ne pas pouvoir l'utilisersubstitution modelest le pouvoir de l'utiliser pour raisonner sur un programme?