Ponieważ z grubsza rozumiem model podstawienia (z przezroczystością referencyjną (RT)), możesz zdekomponować funkcję na jej najprostsze części. Jeśli wyrażenie ma wartość RT, możesz go zdekomponować i zawsze uzyskać ten sam wynik.
Tak, intuicja jest całkiem słuszna. Oto kilka wskazówek, aby uzyskać bardziej precyzyjne:
Jak powiedziałeś, każde wyrażenie RT powinno mieć single
„wynik”. To znaczy, biorąc pod uwagę factorial(5)
wyrażenie w programie, powinno zawsze dawać ten sam „wynik”. Tak więc, jeśli pewna factorial(5)
jest w programie i daje 120, to zawsze powinna dawać 120, niezależnie od tego, która „kolejność kroków” jest rozszerzana / obliczana - niezależnie od czasu .
Przykład: factorial
funkcja.
def factorial(n):
if n == 1:
return 1
return n * factorial(n - 1)
Istnieje kilka uwag do tego wyjaśnienia.
Przede wszystkim należy pamiętać, że różne modele oceny (patrz kolejność aplikacyjna vs. normalna) mogą dawać różne „wyniki” dla tego samego wyrażenia RT.
def first(y, z):
return y
def second(x):
return second(x)
first(2, second(3)) # result depends on eval. model
W powyższym kodzie first
i second
są referencyjnie przezroczyste, a jednak wyrażenie na końcu daje różne „wyniki”, jeśli są oceniane w normalnej kolejności i kolejności stosowania (w tym drugim przypadku wyrażenie się nie zatrzymuje).
.... co prowadzi do użycia „wyniku” w cudzysłowie. Ponieważ wyrażenie nie jest wymagane do zatrzymania, może nie wygenerować wartości. Zatem użycie „wyniku” jest niejasne. Można powiedzieć, że wyrażenie RT zawsze daje to samo computations
w modelu oceny.
Po trzecie, może być wymagane zobaczenie dwóch foo(50)
pojawiających się w programie w różnych lokalizacjach jako różnych wyrażeń - każdy z nich daje własne wyniki, które mogą się od siebie różnić. Na przykład, jeśli język dopuszcza zakres dynamiczny, oba wyrażenia, choć leksykalnie identyczne, są różne. W perlu:
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
}
Zakres dynamiczny wprowadza w błąd, ponieważ ułatwia myślenie, że x
jest to jedyny wkład foo
, podczas gdy w rzeczywistości jest x
i y
. Jednym ze sposobów dostrzeżenia różnicy jest przekształcenie programu w równoważny bez zakresu dynamicznego - to znaczy przekazanie jawnie parametrów, więc zamiast definiować foo(x)
, definiujemy foo(x, y)
i przekazujemy y
jawnie w wywołujących.
Chodzi o to, że zawsze jesteśmy function
nastawieni na myślenie: biorąc pod uwagę pewien wkład wyrażenia, otrzymujemy odpowiedni „wynik”. Jeśli podamy ten sam wkład, zawsze powinniśmy oczekiwać tego samego „wyniku”.
A co z następującym kodem?
def foo():
global y
y = y + 1
return y
y = 10
foo() # yields 11
foo() # yields 12
foo
Postępowanie łamie RT ponieważ istnieje redefinicje. Oznacza to, że zdefiniowaliśmy y
w jednym punkcie, a następnie zdefiniowaliśmy to samo y
. W powyższym przykładzie perla y
s są różnymi powiązaniami, chociaż mają tę samą literę o nazwie „y”. Tutaj y
są w rzeczywistości takie same. Dlatego mówimy, że (ponowne) przypisanie jest metaoperacją : w rzeczywistości zmieniasz definicję swojego programu.
Z grubsza ludzie zwykle przedstawiają różnicę w następujący sposób: w ustawieniu bez efektów ubocznych masz mapowanie od input -> output
. W ustawieniu „imperatywnym” masz input -> ouput
w kontekście coś, state
co może się zmieniać w czasie.
Teraz zamiast po prostu zastępować wyrażenia odpowiadającymi im wartościami, należy również zastosować transformacje do state
każdej operacji, która tego wymaga (i oczywiście wyrażenia mogą się z nimi konsultować, state
aby wykonać obliczenia).
Tak więc, jeśli w programie wolnym od skutków ubocznych wszystko, co musimy wiedzieć, aby obliczyć wyrażenie, to jego indywidualne dane wejściowe, w programie imperatywnym musimy znać dane wejściowe i cały stan dla każdego kroku obliczeniowego. Rozumowanie jest pierwszym, które cierpi z powodu dużego ciosu (teraz, aby debugować problematyczną procedurę, potrzebujesz danych wejściowych i zrzutu pamięci). Niektóre triki są niepraktyczne, jak na przykład zapamiętywanie. Ale współbieżność i równoległość stają się znacznie trudniejsze.
RT
wyłącza was z pomocąsubstitution model.
duży problem ze nie jest w stanie użyćsubstitution model
jest moc używania rozumu o programie?