Reed đã giải thích tại sao ngoại lệ .NET hoạt động khác với ngoại lệ OCaml. Nói chung, ngoại lệ .NET chỉ phù hợp với trường hợp đặc biệt và được thiết kế cho mục đích đó. OCaml có mô hình nhẹ hơn và do đó chúng được sử dụng để thực hiện một số mẫu điều khiển luồng.
Để đưa ra một ví dụ cụ thể, trong OCaml bạn có thể sử dụng ngoại lệ để thực hiện phá vỡ vòng lặp. Ví dụ: giả sử bạn có hàm test
kiểm tra xem một số có phải là số mà chúng tôi muốn hay không. Sau đây lặp qua các số từ 1 đến 100 và trả lại số kết hợp đầu tiên:
// Simple exception used to return the result
exception Returned of int
try
// Iterate over numbers and throw if we find matching number
for n in 0 .. 100 do
printfn "Testing: %d" n
if test n then raise (Returned n)
-1 // Return -1 if not found
with Returned r -> r // Return the result here
Để thực hiện điều này mà không có ngoại lệ, bạn có hai tùy chọn. Bạn có thể viết một hàm đệ quy có hành vi tương tự - nếu bạn gọi find 0
(và nó được biên soạn để về cơ bản mã IL tương tự như sử dụng return n
bên for
vòng lặp trong C#):
let rec find n =
printfn "Testing: %d" n
if n > 100 then -1 // Return -1 if not found
elif test n then n // Return the first found result
else find (n + 1) // Continue iterating
Mã hóa sử dụng các hàm đệ quy có thể có một chút thời gian, nhưng bạn cũng có thể sử dụng các hàm chuẩn được cung cấp bởi thư viện F #. Đây thường là cách tốt nhất để viết lại mã có thể sử dụng các ngoại lệ OCaml cho luồng điều khiển. Trong trường hợp này, bạn có thể viết:
// Find the first value matching the 'test' predicate
let res = seq { 0 .. 100 } |> Seq.tryFind test
// This returns an option type which is 'None' if the value
// was not found and 'Some(x)' if the value was found.
// You can use pattern matching to return '-1' in the default case:
match res with
| None -> -1
| Some n -> n
Nếu bạn không quen thuộc với các loại tùy chọn, hãy xem một số tài liệu giới thiệu. F# wikibook has a good tutorial và MSDN documentation has useful examples.
Sử dụng một chức năng thích hợp từ mô-đun Seq
thường làm cho mã ngắn hơn rất nhiều, do đó, nó là thích hợp hơn. Nó có thể hơi kém hiệu quả hơn so với sử dụng đệ quy trực tiếp, nhưng trong hầu hết các trường hợp, bạn không cần phải lo lắng về điều đó.
EDIT: Tôi tò mò về hiệu suất thực tế. Phiên bản sử dụng Seq.tryFind
sẽ hiệu quả hơn nếu đầu vào là chuỗi được tạo lazily seq { 1 .. 100 }
thay vì danh sách [ 1 .. 100 ]
(do chi phí phân bổ danh sách). Với những thay đổi này, và test
hàm trả về yếu tố thứ 25, thời gian cần thiết để chạy mã 100000 lần trên máy tính của tôi là:
exceptions 2.400sec
recursion 0.013sec
Seq.tryFind 0.240sec
Đây là mẫu cực kỳ tầm thường, vì vậy tôi nghĩ rằng giải pháp sử dụng Seq
sẽ không thường chạy Chậm hơn 10 lần so với mã tương đương được viết bằng cách sử dụng đệ quy. Sự chậm lại có lẽ là do việc phân bổ các cấu trúc dữ liệu bổ sung (đối tượng biểu diễn trình tự, đóng cửa, ...) và cũng do bổ sung thêm (mã cần nhiều cuộc gọi phương thức ảo, thay vì chỉ thao tác số và nhảy đơn giản). Tuy nhiên, các ngoại lệ thậm chí còn tốn kém hơn và không làm cho mã ngắn hơn hoặc dễ đọc hơn theo bất kỳ cách nào ...
Có một [thảo luận về trọng lượng nhẹ của ngoại lệ OCaml] (http://stackoverflow.com/questions/8564025/ocaml-internals-exceptions) –
@JeffreyScofield cảm ơn, tuy nhiên tôi quan tâm nhiều hơn đến các khía cạnh liên quan đến CLR . – gliderkite