Đây không phải là lời giải thích tốt.
"nổi ra" chỉ có nghĩa rằng trong:
\x -> let y = ... in z
nếu ...
không đề cập đến x sau đó nó có thể được trôi ra của lambda:
let y = ... in \x -> z
có nghĩa là nó sẽ chỉ được tính một lần, có thể tiết kiệm rất nhiều thời gian nếu ...
là tốn kém. Tuy nhiên, GHC thận trọng về việc thực hiện tối ưu hóa như thế này, vì chúng có thể giới thiệu rò rỉ không gian. (Mặc dù nó không làm như vậy cho định nghĩa thứ hai nếu bạn cung cấp cho nó một chữ ký kiểu, như Daniel Fischer chỉ ra trong câu trả lời của mình.)
Đây không phải là về tối ưu hóa tự động, mặc dù. Đoạn đầu tiên xác định fib'
bên ngoài lambda, trong khi thứ hai xác định nó bên trong (lambda là tiềm ẩn trong fib x = ...
, tương đương với fib = \x -> ...
), đó là những gì báo giá đang nói.
Tuy nhiên, điều đó không thực sự có liên quan; những gì có liên quan là trong đoạn đầu tiên, map fib' [0 ..]
xảy ra bên ngoài lambda, và do đó kết quả của nó được chia sẻ giữa tất cả các ứng dụng của lambda (trong mã đó, "lambda" phát sinh từ ứng dụng một phần của (!!)
). Trong phần sau, nó nằm bên trong lambda và rất có khả năng được tính toán lại cho mọi ứng dụng của fib
.
Kết quả cuối cùng là việc triển khai trước đây lưu trữ các giá trị và hiệu quả hơn rất nhiều so với sau. Lưu ý rằng hiệu quả của đoạn đầu tiên phụ thuộc vào thực tế là fib'
không recurse trực tiếp, nhưng thay vì thông qua fib
, và do đó lợi ích từ các memoisation.
Nó liên quan đến việc mở rộng eta; đoạn mã thứ hai là phần mở rộng eta đầu tiên. Nhưng tuyên bố bạn trích dẫn không giải thích những gì đang xảy ra ở tất cả.
Lưu ý rằng đây là hành vi cụ thể của việc triển khai chứ không phải là một phần ngữ nghĩa của Haskell. Tuy nhiên, tất cả các triển khai hợp lý sẽ hoạt động theo cách này.
Câu trả lời này và câu trả lời của Daniel Fischer bây giờ là đệ quy lẫn nhau. – misterbee
@misterbee: may mắn thay, chỉ những lập trình viên của Haskell mới đọc được, và chúng tôi lười biếng, đúng không? – leftaroundabout