2012-02-17 33 views
29

thể trùng lặp:
Why are functions in Ocaml/F# not recursive by default?Tại sao ocaml cần cả "let" và "let rec"?

OCaml sử dụng let để xác định một chức năng mới, hoặc let rec để xác định một chức năng đó là đệ quy. Tại sao nó cần cả hai điều này - chúng ta không thể sử dụng let cho mọi thứ?

Ví dụ, để định nghĩa một hàm phi đệ quy kế và thừa đệ quy trong OCaml (trên thực tế, trong các phiên dịch OCaml) Tôi có thể viết

let succ n = n + 1;; 

let rec fact n = 
    if n = 0 then 1 else n * fact (n-1);; 

Trong khi trong Haskell (GHCI) tôi có thể viết

let succ n = n + 1 

let fact n = 
    if n == 0 then 1 else n * fact (n-1) 

Tại sao OCaml phân biệt giữa letlet rec? Có phải đó là vấn đề về hiệu suất, hay điều gì đó tinh tế hơn?

+6

Tại sao phiếu bầu đóng lại ..? Có vẻ như một câu hỏi hợp lý. – Sean

+0

Xem http://stackoverflow.com/questions/900585/why-are-functions-in-ocaml-f-not-recursive-by-default –

+0

@Sean: đây là một câu hỏi thường gặp. – Ashe

Trả lời

27

Vâng, có cả hai có sẵn thay vì chỉ có một cho phép lập trình kiểm soát chặt chẽ hơn trên phạm vi. Với let x = e1 in e2, sự ràng buộc chỉ xuất hiện trong môi trường của e2, trong khi với let rec x = e1 in e2 ràng buộc có mặt trong cả hai môi trường e1e2.

(Edit: Tôi muốn nhấn mạnh rằng đó là không một vấn đề hiệu suất, mà làm cho có sự khác biệt nào cả.)

Dưới đây là hai tình huống mà có này ràng buộc phi đệ quy là hữu ích:

  • che giấu định nghĩa hiện tại bằng tinh lọc sử dụng liên kết cũ. Một cái gì đó như: let f x = (let x = sanitize x in ...), trong đó sanitize là một hàm đảm bảo đầu vào có một số thuộc tính mong muốn (ví dụ: nó lấy chuẩn của một vectơ có thể không chuẩn hóa, v.v.). Điều này rất hữu ích trong một số trường hợp.

  • lập trình meta, ví dụ như viết macro. Hãy tưởng tượng tôi muốn xác định một macro SQUARE(foo) mà desugars vào let x = foo in x * x, cho bất kỳ biểu thức foo. Tôi cần sự ràng buộc này để tránh trùng lặp mã trong đầu ra (tôi không muốn SQUARE(factorial n) để tính factorial n hai lần). Điều này chỉ hợp vệ sinh nếu ràng buộc let không đệ quy, nếu không tôi không thể viết let x = 2 in SQUARE(x) và nhận kết quả chính xác.

Vì vậy, tôi cho rằng điều rất quan trọng là phải có cả ràng buộc đệ quy và không đệ quy. Bây giờ, hành vi mặc định của sự ràng buộc là một vấn đề của quy ước. Bạn có thể nói rằng let x = ... là đệ quy, và người ta phải sử dụng let nonrec x = ... để có được chất kết dính không đệ quy. Chọn một mặc định hoặc khác là một vấn đề mà phong cách lập trình bạn muốn ủng hộ và có những lý do tốt để thực hiện một trong hai lựa chọn. Haskell bị lỗi do không có chế độ không đệ quy này và OCaml có lỗi chính xác ở mức loại: type foo = ... là đệ quy và không có tùy chọn không đệ quy - xem this blog post.

¹: khi Google Code Search có sẵn, tôi đã sử dụng nó để tìm kiếm trong mã Haskell cho mẫu let x' = sanitize x in .... Đây là cách giải quyết thông thường khi không có sự ràng buộc không đệ quy, nhưng ít an toàn hơn vì bạn có nguy cơ viết x thay vì x' do nhầm lẫn sau này - trong một số trường hợp bạn muốn có sẵn, vì vậy hãy chọn một tên khác có thể là tự nguyện . Thành ngữ tốt sẽ là sử dụng tên biến dài hơn cho x đầu tiên, chẳng hạn như unsanitized_x. Dù sao, chỉ cần tìm kiếm x' trên mặt đất (không có tên biến khác) và x1 đã chuyển rất nhiều kết quả. Erlang (và tất cả các ngôn ngữ mà cố gắng làm cho biến bóng khó khăn: Coffeescript, vv) có vấn đề thậm chí còn tồi tệ hơn của loại hình này.

Điều đó nói rằng, sự lựa chọn có ràng buộc Haskell đệ quy theo mặc định (chứ không phải đệ quy) chắc chắn có ý nghĩa, vì nó phù hợp với đánh giá lười biếng theo mặc định, giúp dễ dàng xây dựng các giá trị đệ quy. -ngôn ngữ mặc định có nhiều hạn chế hơn về định nghĩa đệ quy nào có ý nghĩa.

+2

Bài đăng hay, nhưng tôi không cảm thấy thích thú với điều "lập trình meta". Người ta có thể tranh luận để viết 'square' như là một hàm và khi trình biên dịch của bạn có khả năng nội tuyến, sau đó nó sẽ hoạt động hiệu quả như một macro, hoàn chỉnh với alpha-renaming và tất cả những thứ đó. – Ingo

+3

Ngoài ra, vấn đề ẩn có thể được thực hiện dễ dàng nhất với các thành ngữ như: 'foo x = work (sanitized x) ở nơi x = ....' – Ingo

+0

@Ingo, metaprogramming không chỉ đơn giản là mở rộng các hàm chung. –

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