Phần phức tạp là vòng lặp. Hãy để chúng tôi bắt đầu với điều đó. Một vòng lặp thường được chuyển đổi thành kiểu chức năng bằng cách thể hiện phép lặp với một hàm duy nhất. Lặp lại là một biến đổi của biến vòng lặp.
Đây là một thực hiện chức năng của một vòng lặp chung:
loop : v -> (v -> v) -> (v -> Bool) -> v
loop init iter cond_to_cont =
if cond_to_cont init
then loop (iter init) iter cond
else init
Nó nhận (một giá trị ban đầu của biến vòng lặp, hàm biểu thị một lần lặp duy nhất [trên biến vòng lặp]) (một điều kiện để tiếp tục vòng lặp).
Ví dụ của bạn sử dụng một vòng lặp trên một mảng, cũng phá vỡ. Khả năng này trong ngôn ngữ mệnh lệnh của bạn được đưa vào chính ngôn ngữ đó. Trong lập trình chức năng, khả năng như vậy thường được thực hiện ở cấp thư viện. Đây là một triển khai có thể
module Array (foldlc) where
foldlc : v -> (v -> e -> v) -> (v -> Bool) -> Array e -> v
foldlc init iter cond_to_cont arr =
loop
(init, 0)
(λ (val, next_pos) -> (iter val (at next_pos arr), next_pos + 1))
(λ (val, next_pos) -> and (cond_to_cont val) (next_pos < size arr))
Trong đó :
Tôi sử dụng cặp ((val, next_pose)) chứa biến vòng lặp hiển thị bên ngoài và vị trí trong mảng, hàm này ẩn.
Hàm lặp phức tạp hơn một chút so với trong vòng lặp chung, phiên bản này cho phép sử dụng phần tử hiện tại của mảng. [Nó ở dạng quăn .]
Các chức năng như vậy thường được đặt tên là "gấp".
Tôi đặt một chữ "l" trong tên để chỉ ra rằng việc tích lũy các phần tử của mảng được thực hiện theo cách liên kết trái; để bắt chước thói quen của các ngôn ngữ lập trình bắt buộc để lặp lại một mảng từ chỉ số thấp đến cao.
Tôi đặt một chữ "c" trong tên để chỉ ra rằng phiên bản gấp này có một điều kiện kiểm soát nếu và khi vòng lặp được dừng sớm.
Tất nhiên các chức năng tiện ích như vậy có khả năng sẽ có sẵn trong thư viện cơ sở được cung cấp với ngôn ngữ lập trình chức năng được sử dụng. Tôi đã viết chúng ở đây để trình diễn.
Bây giờ chúng ta có tất cả các công cụ bằng ngôn ngữ trong trường hợp bắt buộc, chúng ta có thể chuyển sang thực hiện chức năng cụ thể của ví dụ của bạn.
Biến trong vòng lặp của bạn là một cặp ('answer', boolean mã hóa xem có tiếp tục không).
iter : (Int, Bool) -> Int -> (Int, Bool)
iter (answer, cont) collection_element =
let new_answer = answer + collection_element
in case new_answer of
10 -> (new_answer, false)
150 -> (new_answer + 100, true)
_ -> (new_answer, true)
Lưu ý rằng tôi đã sử dụng một "biến" mới 'new_answer'. Điều này là do trong lập trình chức năng, tôi không thể thay đổi giá trị của một "biến" đã được khởi tạo. Tôi không lo lắng về hiệu suất, trình biên dịch có thể sử dụng lại bộ nhớ của 'answer' cho 'new_answer' thông qua phân tích trọn đời, nếu nó cho rằng nó hiệu quả hơn.
Kết hợp điều này vào chức năng lặp của chúng tôi đã phát triển trước đó:
doSomeCalc :: Array Int -> Int
doSomeCalc arr = fst (Array.foldlc (0, true) iter snd arr)
"Mảng" ở đây là tên mô-đun xuất hàm Foldlc.
"fist", "second" là viết tắt của các hàm trả về thành phần thứ nhất, thứ hai của tham số cặp của nó
fst : (x, y) -> x
snd : (x, y) -> y
Trong trường hợp này, kiểu "không có điểm" làm tăng khả năng đọc của việc thực hiện doSomeCalc:
doSomeCalc = Array.foldlc (0, true) iter snd >>> fst
(>>>) là thành phần chức năng: (>>>) : (a -> b) -> (b -> c) -> (a -> c)
Nó giống như trên, chỉ là tham số "mảng" được bỏ qua từ cả hai phía của phương trình xác định.
Một điều cuối cùng: kiểm tra trường hợp (mảng == null). Trong các ngôn ngữ lập trình được thiết kế tốt hơn, nhưng ngay cả trong các ngôn ngữ được thiết kế tồi với một số môn học cơ bản, người ta sử dụng một loại tùy chọn để thể hiện sự không tồn tại. Điều này không liên quan nhiều đến lập trình chức năng, mà câu hỏi cuối cùng là về, do đó tôi không giải quyết nó.
break
vàreturn answer
có thể được thay thế bằng mộtreturn
vòng lặp. Trong FP, bạn có thể thực hiện việc hoàn trả sớm này bằng cách sử dụng các phần tiếp theo, xem ví dụ en.wikipedia.org/wiki/Contininating