(Sửa từ this post trên fshub)
Lần đầu tiên tôi đã đi để đạt được cho nghỉ/tiếp tục trong OCaml/F #, nó ném tôi cho một (vô hạn) vòng lặp, do đó, để nói chuyện, bởi vì không có điều như vậy tồn tại! Trong OCaml, người ta có thể sử dụng ngoại lệ để thoát khỏi vòng lặp vì chúng là rất giá rẻ, nhưng trong F # (in.NET) chi phí khá cao và không hữu ích cho việc kiểm soát luồng "bình thường".
Điều này xuất hiện khi chơi với thuật toán sắp xếp một thời gian trở lại (để giết thời gian), sử dụng nhiều lần lặp lại/cho đến khi và ngắt. Nó đánh tôi rằng các hàm gọi đệ quy đuôi có thể đạt được kết quả tương tự, chỉ với một chút ding để dễ đọc. Vì vậy, tôi đã ném ra 'mutable bDone' và 'while not bDone' và cố gắng viết mã mà không có những cấu trúc bắt buộc này. Các phần sau chỉ ra các phần lặp và cho thấy cách viết lặp lại/cho đến khi, làm/while, trong khi/do, ngắt/tiếp tục và mã kiểu thử nghiệm ở giữa bằng cách sử dụng các ổ đĩa. Tất cả những điều này xuất hiện để chạy với tốc độ tương tự như câu lệnh truyền thống F # 'while', nhưng số dặm của bạn có thể thay đổi (một số nền tảng có thể không thực hiện đúng tailcall và do đó có thể ngăn lỗi cho đến khi chúng được vá). Cuối cùng là một thuật toán sắp xếp (xấu) được viết bằng cả hai kiểu, để so sánh. Hãy bắt đầu với một vòng lặp 'do/while', được viết theo kiểu F # truyền thống, sau đó xem xét các biến thể chức năng cung cấp cả cùng một loại vòng lặp, cũng như ngữ nghĩa khác nhau như trong khi/làm, lặp lại/cho đến khi, kiểm tra ở giữa, và thậm chí phá vỡ/tiếp tục (không có monads .. um, quy trình công việc!).
#light
(* something to work on... *)
let v = ref 0
let f() = incr v;
let g() = !v;
let N = 100000000
let imperDoWhile() =
let mutable x = 0
let mutable bDone = false
while not bDone do
f()
x <- x + 1
if x >= N then bDone <- true
Ok, thật dễ dàng. Hãy nhớ rằng f() luôn được gọi ít nhất một lần (do/while).
Đây là cùng mã, nhưng theo kiểu chức năng. Lưu ý rằng chúng tôi không cần khai báo một biến thể ở đây.
let funDoWhile() =
let rec loop x =
f() (*Do*)
if x < N then (*While*)
loop (x+1)
loop 0
Chúng tôi có thể quay điều đó sang kiểu truyền thống/trong khi bằng cách thực hiện cuộc gọi hàm bên trong khối if.
let funWhileDo() =
let rec loop x =
if x < N then (*While*)
f() (*Do*)
loop (x+1)
loop 0
Làm thế nào để lặp lại khối cho đến khi một số điều kiện là đúng (lặp lại/đến)? Đủ dễ dàng ...
let funRepeatUntil() =
let rec loop x =
f() (*Repeat*)
if x >= N then() (*Until*)
else loop (x+1)
loop 0
Điều gì đã xảy ra trong thời gian nghỉ ngắn hơn? Vâng, chỉ cần giới thiệu một biểu thức có điều kiện trả về 'đơn vị', như trong:
let funBreak() =
let rec loop() =
let x = g()
if x > N then() (*break*)
else
f()
loop()
loop()
Còn tiếp tục? Vâng, đó chỉ là một cuộc gọi khác để lặp lại! Thứ nhất, với một cái nạng cú pháp:
let funBreakContinue() =
let break'() =()
let rec continue'() =
let x = g()
if x > N then break'()
elif x % 2 = 0 then
f(); f(); f();
continue'()
else
f()
continue'()
continue'()
Và sau đó một lần nữa mà không (xấu xí) cú pháp cái nạng:
let funBreakContinue'() =
let rec loop() =
let x = g()
if x > N then()
elif x % 2 = 0 then
f(); f(); f();
loop()
else
f()
loop()
loop()
dễ dàng như chiếc bánh!
Một kết quả tốt đẹp của các biểu mẫu vòng lặp này là giúp dễ dàng phát hiện và triển khai các trạng thái trong vòng lặp của bạn. Ví dụ: loại bong bóng liên tục lặp lại trên toàn bộ mảng, trao đổi các giá trị không phù hợp khi tìm thấy chúng. Nó theo dõi xem việc vượt qua mảng có tạo ra bất kỳ trao đổi nào hay không. Nếu không, thì mọi giá trị phải ở đúng nơi, do đó sắp xếp có thể chấm dứt. Là một tối ưu hóa, trên mỗi lần truyền thông qua mảng, giá trị cuối cùng trong mảng kết thúc được sắp xếp vào đúng vị trí. Vì vậy, vòng lặp có thể được rút ngắn từng cái một. Hầu hết các thuật toán kiểm tra trao đổi và cập nhật cờ "bModified" mỗi khi có. Tuy nhiên, một khi việc hoán đổi đầu tiên được thực hiện, không cần phải có nhiệm vụ đó; nó đã được đặt thành true!
Dưới đây là mã F # thực hiện một loại bong bóng (có, loại bong bóng là thuật toán khủng khiếp, đá quicksort). Cuối cùng là một thực thi bắt buộc mà không thay đổi trạng thái; nó cập nhật cờ bModified cho mọi trao đổi.Điều thú vị là giải pháp bắt buộc nhanh hơn trên các mảng nhỏ và chỉ chậm hơn một hoặc hai phần trăm trên các mảng lớn. (Thực hiện cho một ví dụ tốt, mặc dù).
let inline sort2 f i j (a:'a array) =
let i' = a.[ i ]
let j' = a.[ j ]
if f i' j' > 0 then
a.[ i ] <- j'
a.[ j ] <- i'
let bubble f (xs:'a array) =
if xs.Length = 0
then()
let rec modified i endix =
if i = endix then
unmodified 0 (endix-1)
else
let j = i+1
sort2 f i j xs
modified j endix
and unmodified i endix =
if i = endix then
()
else
let j = i+1
let i' = xs.[ i ]
let j' = xs.[ j ]
if f i' j' > 0 then
xs.[ i ] <- j'
xs.[ j ] <- i'
modified j endix
else
unmodified j endix
in unmodified 0 (xs.Length-1)
let bubble_imperitive f (xs:'a array) =
let mutable bModified = true
let mutable endix = xs.Length - 1
while bModified do
bModified <- false
endix <- endix - 1
for i in 0..endix do
let j = i+1
let i' = xs.[ i ]
let j' = xs.[ j ]
if f i' j' > 0 then
xs.[ i ] <- j'
xs.[ j ] <- i'
bModified <- true
done
done
Có lẽ nên là cộng đồng wiki - không có "câu trả lời" cho mỗi gia nhập? – Anthony
@ Anthony, tôi hy vọng có một trang web, nhưng nếu không có, thì tôi sẽ làm cái này. – Unknown
Dường như bạn đã tạo cộng đồng này quá sớm - hãy xem pleac-ocaml – Thelema