Khi tôi gần như hiểu mô hình thay thế (với độ trong suốt tham chiếu (RT)), bạn có thể hủy cấu trúc một hàm thành các phần đơn giản nhất của nó. Nếu biểu thức là RT, thì bạn có thể hủy biểu thức và luôn nhận được kết quả tương tự.
Vâng, trực giác hoàn toàn đúng. Dưới đây là một vài gợi ý để có được chính xác hơn:
Giống như bạn đã nói, bất kỳ biểu thức RT nào cũng phải có single
"kết quả". Đó là, được đưa ra một factorial(5)
biểu thức trong chương trình, nó sẽ luôn mang lại cùng một "kết quả". Vì vậy, nếu một số nhất định factorial(5)
có trong chương trình và nó mang lại 120, thì nó sẽ luôn mang lại 120 bất kể "thứ tự bước" nào được mở rộng / tính toán - bất kể thời gian .
Ví dụ: factorial
hàm.
def factorial(n):
if n == 1:
return 1
return n * factorial(n - 1)
Có một vài cân nhắc với lời giải thích này.
Trước hết, hãy ghi nhớ các mô hình đánh giá khác nhau (xem thứ tự áp dụng so với bình thường) có thể mang lại "kết quả" khác nhau cho cùng một biểu thức RT.
def first(y, z):
return y
def second(x):
return second(x)
first(2, second(3)) # result depends on eval. model
Trong đoạn mã trên, first
và second
được minh bạch tham chiếu, tuy nhiên, biểu thức ở cuối mang lại "kết quả" khác nhau nếu được đánh giá theo thứ tự thông thường và thứ tự áp dụng (theo biểu thức sau, biểu thức không dừng lại).
.... dẫn đến việc sử dụng "kết quả" trong dấu ngoặc kép. Vì nó không bắt buộc phải có biểu thức để dừng lại, nên nó có thể không tạo ra giá trị. Vì vậy, sử dụng "kết quả" là loại mờ. Người ta có thể nói một biểu thức RT luôn mang lại kết quả tương tự computations
theo mô hình đánh giá.
Thứ ba, có thể phải thấy hai người foo(50)
xuất hiện trong chương trình ở các địa điểm khác nhau dưới dạng các biểu thức khác nhau - mỗi người cho kết quả riêng có thể khác nhau. Chẳng hạn, nếu ngôn ngữ cho phép phạm vi động, cả hai biểu thức, mặc dù giống nhau về mặt từ vựng, đều khác nhau. Trong 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
}
Phạm vi động đánh lừa bởi vì nó giúp người ta dễ dàng nghĩ x
là đầu vào duy nhất foo
, trong thực tế, nó là x
và y
. Một cách để thấy sự khác biệt là chuyển đổi chương trình thành một chương trình tương đương không có phạm vi động - nghĩa là truyền các tham số một cách rõ ràng, vì vậy thay vì xác định foo(x)
, chúng tôi xác định foo(x, y)
và chuyển y
một cách rõ ràng trong các trình gọi.
Vấn đề là, chúng ta luôn ở trong một function
tư duy: đưa ra một đầu vào nhất định cho một biểu thức, chúng ta được cung cấp một "kết quả" tương ứng. Nếu chúng ta đưa ra cùng một đầu vào, chúng ta sẽ luôn mong đợi cùng một "kết quả".
Bây giờ, những gì về mã sau đây?
def foo():
global y
y = y + 1
return y
y = 10
foo() # yields 11
foo() # yields 12
Các foo
thủ tục phá vỡ RT vì có định nghĩa lại. Nghĩa là, chúng ta định nghĩa y
trong một thời điểm, và sau trên, xác định lại rằng cùng y
. Trong ví dụ perl ở trên, y
s là các ràng buộc khác nhau mặc dù chúng có cùng tên chữ "y". Ở đây các y
s thực sự giống nhau. Đó là lý do tại sao chúng tôi nói (tái) gán là một hoạt động meta : trên thực tế bạn đang thay đổi định nghĩa về chương trình của bạn.
Một cách thô bạo, mọi người thường mô tả sự khác biệt như sau: trong cài đặt miễn phí hiệu ứng phụ, bạn có một ánh xạ từ input -> output
. Trong cài đặt "bắt buộc", bạn có input -> ouput
bối cảnh state
có thể thay đổi theo thời gian.
Bây giờ, thay vì chỉ thay thế các biểu thức cho các giá trị tương ứng của chúng, người ta cũng phải áp dụng các phép biến đổi cho state
mỗi thao tác yêu cầu nó (và tất nhiên, các biểu thức có thể tham khảo ý kiến tương tự state
để thực hiện tính toán).
Vì vậy, nếu trong một chương trình miễn phí có hiệu lực phụ, tất cả những gì chúng ta cần biết để tính toán một biểu thức là đầu vào riêng lẻ của nó, thì trong một chương trình bắt buộc, chúng ta cần biết các đầu vào và toàn bộ trạng thái, cho từng bước tính toán. Lý luận là người đầu tiên chịu một cú đánh lớn (bây giờ, để gỡ lỗi một thủ tục có vấn đề, bạn cần đầu vào và kết xuất lõi). Một số thủ thuật được đưa ra không thực tế, như ghi nhớ. Nhưng đồng thời, sự đồng thời và song song trở nên thách thức hơn nhiều.
RT
không cho phép bạn sử dụngsubstitution model.
Vấn đề lớn với việc không thể sử dụngsubstitution model
là sức mạnh của việc sử dụng nó để lý do về một chương trình?