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: factorialfunkcja.
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 firsti secondsą 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 computationsw 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 xjest to jedyny wkład foo, podczas gdy w rzeczywistości jest xi 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 yjawnie w wywołujących.
Chodzi o to, że zawsze jesteśmy functionnastawieni 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
fooPostępowanie łamie RT ponieważ istnieje redefinicje. Oznacza to, że zdefiniowaliśmy yw jednym punkcie, a następnie zdefiniowaliśmy to samo y . W powyższym przykładzie perla ys są różnymi powiązaniami, chociaż mają tę samą literę o nazwie „y”. Tutaj ysą 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 -> ouputw kontekście coś, stateco 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 statekażdej operacji, która tego wymaga (i oczywiście wyrażenia mogą się z nimi konsultować, stateaby 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.
RTwyłącza was z pomocąsubstitution model.duży problem ze nie jest w stanie użyćsubstitution modeljest moc używania rozumu o programie?