2012-03-25 21 views
6

Làm thế nào là nó có phù hợp dưới đây ở phía bên phải của phương trình ta có thể sử dụng biểu tượng 'fibs' mặc dù nó vẫn chưa definied:Sử dụng biểu tượng trước khi nó được definied

let fibs = 0 : 1 : zipWith (+) fibs (tail fibs) 
+0

@NiklasB. Tôi biết đệ quy là gì nhưng tôi không hiểu một lớp lót cụ thể nào. Xem bình luận của tôi dưới câu trả lời của GManNickG. – Trismegistos

+0

@NiklasB. Hầu hết các ngôn ngữ làm cho bạn nhảy qua một vài hoops để sử dụng một _value_ trong định nghĩa riêng của nó, mặc dù. Một số ít có sự lười biếng được xây dựng. –

+0

@Daniel: Vâng, đúng, hầu hết các ngôn ngữ chỉ hỗ trợ đệ quy cho các chức năng, không phải cho danh sách. –

Trả lời

19

Vấn đề là định nghĩa của fibs

fibs = 0 : 1 : zipWith (+) fibs (tail fibs) 

là không được đánh giá cho đến khi nó được sử dụng ở một nơi khác. Sau đó, định nghĩa mở ra bằng cách sử dụng một phần đã biết. Chúng tôi bắt đầu với fibs = 0 : 1 : ???. Sau đó, nếu các yếu tố thứ ba là bao giờ cần thiết, định nghĩa được đánh giá thêm một bước nữa,

fibs = 0 : 1 : zipWith (+) (0 : 1 : ???) (tail (0 : 1 : ???)) 
    = 0 : 1 : zipWith (+) (0 : 1 : ???) (1 : ???) 
    = 0 : 1 : (0 + 1) : zipWith (+) (1 : ???) (???) 

nhưng sau đó các phần chưa biết ??? đã trở thành một phần nổi tiếng, nó đã được xác định là ??? = 1 : ????, do đó diễn ra có thể tiếp tục,

 = 0 : 1 : 1 : zipWith (+) (1 : 1 : ????) (1 : ????) 
    = 0 : 1 : 1 : 2 : zipWith (+) (1 : ????) (????) 
    -- now ???? is known to be 2:????? 
    = 0 : 1 : 1 : 2 : zipWith (+) (1 : 2 : ?????) (2 : ?????) 

, vv

+0

Cảm ơn, ngay từ cái nhìn đầu tiên nó trông phức tạp hơn đệ quy hàm. Có một số ràng buộc về cú pháp tính toán danh sách/giá trị đệ quy không? Nói cách khác, nhưng không phải là một cách dễ hiểu, cú pháp được phép là gì? – Trismegistos

+1

Không có giới hạn cú pháp, bạn chỉ cần sử dụng giá trị được định nghĩa trong biểu thức được định nghĩa là 'value = some expression using value'. Điểm cần lưu ý là liệu tính toán có chấm dứt hay không. Ví dụ, bạn có thể thử định nghĩa 'cycle xs = let result = result ++ xs trong kết quả'. Nhưng nếu bạn cố gắng đánh giá nó, trước tiên bạn phải tìm hiểu xem liệu 'kết quả 'là không rỗng, để biết phương trình nào được sử dụng cho' (++) '. Vì vậy, bạn nhìn vào định nghĩa của 'result',' result = result ++ xs', do đó, để tìm hiểu xem liệu 'result' có phải là nonempty hay không, bạn phải tìm hiểu xem liệu' result' là nonempty ... –

+1

Nhưng nếu bạn đã định nghĩa nó Kết quả 'cycle xs = let result = xs ++ trong kết quả', bạn đã biết đủ' kết quả' không bị kẹt khi cần kiểm tra 'result' phát sinh trong việc tính' kết quả' (trừ khi 'xs' trống , điều đó sẽ đưa bạn vào trường hợp 'let x = x in x'). Vì vậy, một định nghĩa tự tham chiếu như vậy đòi hỏi một phần của 'giá trị' có thể được xác định trước khi kiểm tra nó trong biểu thức xác định, và phần cần thiết tiếp theo luôn có thể được xác định bằng cách sử dụng cái đã biết. –

6

Nó sẽ không thực sự cố gắng gọi fibs trong định nghĩa của bạn cho đến khi một cái gì đó khác sử dụng fibs sau này trong chương trình của bạn, tại thời điểm đó fibs đã được xác định hoàn toàn.

Bạn có thể làm điều này trong hầu hết các ngôn ngữ khác quá:

int foo(int x) 
{ 
    if (x <= 0) return 0; 

    // will call foo when it gets there, at which point its been defined 
    foo(x - 1); 
} 
+0

Tôi hiểu sự tái diễn trong các ngôn ngữ bắt buộc và cũng trong haskell khi nó được chia nhỏ thành vài dòng. ví dụ: fib 0 = 0; fib 1 = 1; fib n = fib (n - 1) + fib (n - 2). Tôi không hiểu cách nó được sử dụng trong ví dụ tôi đã đưa ra trong câu hỏi chính. – Trismegistos

+1

@Trismegistos: Hoàn toàn giống nhau, Haskell không cần thực hiện chức năng này cho đến khi nó được gọi. Khi bạn gọi 'fibs' ở đâu đó khác, và trong cuộc gọi đó nó đạt tới 'fibs', nó chỉ là qui trình ngược lại. Haskell không cần phải gọi nó hoặc bất cứ điều gì trong khi nó đang được xác định/phân tích cú pháp. – GManNickG

4

Tất cả bindings Haskell là đệ quy. Điều này khác với hầu hết các ngôn ngữ, nhưng nó thường hoạt động chính xác do sự lười biếng (đánh giá Haskell là không nghiêm ngặt, ngược lại với hầu hết các ngôn ngữ phổ biến). Người mới thường vấp lên khi họ cố gắng một cái gì đó như:

main = do 
    let a = 3 
    let a = 3 + a 
    print a 

Bởi vì thứ hai ràng buộc để a thực bỏ qua của và bóng là người đầu tiên, và xác định a về bản thân, gây ra một vòng lặp vô hạn khi bạn cố gắng in ra kết quả của 3 + 3 + 3 + 3 + ...

một ví dụ đơn giản của một danh sách vô hạn là ones: một danh sách vô hạn của 1 s

ones = 1 : ones 

Trong trường hợp này, những người chỉ đơn giản đề cập đến bản thân

_______ 
    |  | 
    v  | 
________ | 
| ones | | 
| 1 : ---| 
-------- 

Trong Haskell, bạn có thể tạo ra một cây vô hạn trong nhiều cùng một cách mà bạn có thể tạo một danh sách vô hạn:

data Tree a = Stub | Branch a (Tree a) (Tree a) 
onesTree = Branch 1 onesTree onesTree 


______ _______ 
| | |  | 
| v v  | 
| ____________ | 
| | onesTree | | 
|--- | 1 | ----| 
    ------------ 

Tôi nghĩ rằng câu hỏi thực sự là: tại sao các ngôn ngữ khác không hỗ trợ các giá trị đệ quy một cách thuận tiện như Haskell?

1

Vâng, để hiểu điều này, bạn nên hiểu cách đánh giá mức độ lười biếng được thực hiện. Về cơ bản, các biểu thức chưa được đánh giá được biểu thị bằng khối: một cấu trúc dữ liệu đại diện cho tất cả thông tin cần thiết để tính toán giá trị khi nó thực sự cần thiết.Khi điều này xảy ra (hoặc khi chúng ta nói, khi đoạn thunk là buộc), mã để tính toán giá trị được thực hiện, và nội dung của thunk được thay thế bằng kết quả - có thể có con trỏ tới các khối khác.

Vì vậy, fibs bắt đầu dưới dạng một đoạn. Thunk này chứa các con trỏ tới mã được sử dụng để tính toán giá trị của nó và các con trỏ tới các khối mà mã này lấy làm đối số. Một trong những con trỏ sau này là một con trỏ tới số fibs.

Các vấn đề liên quan