ส่วนที่ซับซ้อนคือการวนซ้ำ ให้เราเริ่มด้วยสิ่งนั้น โดยปกติแล้วการวนซ้ำจะถูกแปลงเป็นลักษณะการทำงานโดยแสดงการวนซ้ำด้วยฟังก์ชันเดียว การวนซ้ำเป็นการแปลงตัวแปรลูป
นี่คือการใช้งานของลูปทั่วไป:
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
ใช้เวลา (ค่าเริ่มต้นของตัวแปรลูปฟังก์ชันที่แสดงการวนซ้ำเดียว [บนตัวแปรลูป]) (เงื่อนไขเพื่อดำเนินการต่อลูป)
ตัวอย่างของคุณใช้การวนซ้ำบนอาร์เรย์ซึ่งจะแบ่ง ความสามารถนี้ในภาษาที่จำเป็นของคุณถูกทำให้เป็นภาษานั้นเอง ในการเขียนโปรแกรมการทำงานฟังก์ชั่นดังกล่าวมักจะนำมาใช้ในระดับห้องสมุด นี่คือการดำเนินการที่เป็นไปได้
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))
ในนั้น :
ฉันใช้คู่ ((val, next_pos)) ซึ่งมีตัวแปรลูปที่มองเห็นภายนอกและตำแหน่งในอาเรย์ซึ่งฟังก์ชั่นนี้ซ่อนอยู่
ฟังก์ชั่นการวนซ้ำนั้นซับซ้อนกว่าลูปทั่วไปเล็กน้อยรุ่นนี้ทำให้สามารถใช้องค์ประกอบปัจจุบันของอาร์เรย์ได้ [มันอยู่ในรูปแบบcurried ]
ฟังก์ชั่นดังกล่าวมักจะมีชื่อว่า "fold"
ฉันใส่ "l" ในชื่อเพื่อระบุว่าการสะสมขององค์ประกอบของอาเรย์นั้นทำได้ในลักษณะที่สัมพันธ์กันทางซ้าย เพื่อเลียนแบบนิสัยของภาษาการเขียนโปรแกรมที่จำเป็นเพื่อย้ำอาร์เรย์จากดัชนีต่ำถึงสูง
ฉันใส่ "c" ในชื่อเพื่อระบุว่า fold รุ่นนี้มีเงื่อนไขที่ควบคุมว่าและเมื่อจะหยุดลูปก่อนหรือไม่
แน่นอนว่าฟังก์ชั่นยูทิลิตี้ดังกล่าวมีแนวโน้มว่าจะพร้อมใช้งานในไลบรารีฐานที่มาพร้อมกับภาษาการเขียนโปรแกรมการทำงานที่ใช้ ฉันเขียนพวกเขาที่นี่เพื่อสาธิต
ตอนนี้เรามีเครื่องมือทั้งหมดที่เป็นภาษาในกรณีจำเป็นเราสามารถหันไปใช้ฟังก์ชั่นเฉพาะของตัวอย่างของคุณ
ตัวแปรในลูปของคุณเป็นคู่ ('answer', บูลีนที่เข้ารหัสว่าจะดำเนินการต่อ)
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)
โปรดทราบว่าฉันใช้ "ตัวแปร" ใหม่ new_answer ' นี่เป็นเพราะในการเขียนโปรแกรมการทำงานฉันไม่สามารถเปลี่ยนค่าของ "ตัวแปร" เริ่มต้นแล้ว ฉันไม่กังวลเกี่ยวกับประสิทธิภาพคอมไพเลอร์อาจนำหน่วยความจำของ 'คำตอบ' สำหรับ 'new_answer' กลับมาใช้ใหม่ผ่านการวิเคราะห์ตลอดชีวิตหากคิดว่ามีประสิทธิภาพมากขึ้น
การรวมสิ่งนี้เข้ากับฟังก์ชั่นการวนซ้ำของเราที่พัฒนาขึ้นก่อนหน้านี้:
doSomeCalc :: Array Int -> Int
doSomeCalc arr = fst (Array.foldlc (0, true) iter snd arr)
"Array" ที่นี่คือชื่อโมดูลที่ส่งออกฟังก์ชัน foldlc
"fist", "second" หมายถึงฟังก์ชั่นที่ส่งกลับองค์ประกอบที่หนึ่งและสองของพารามิเตอร์ pair
fst : (x, y) -> x
snd : (x, y) -> y
ในกรณีนี้สไตล์ "ไม่มีจุด" จะเพิ่มความสามารถในการอ่านของการใช้ doSomeCalc:
doSomeCalc = Array.foldlc (0, true) iter snd >>> fst
(>>>) คือฟังก์ชั่นองค์ประกอบ: (>>>) : (a -> b) -> (b -> c) -> (a -> c)
มันเป็นเช่นเดียวกับข้างต้นเพียงแค่พารามิเตอร์ "arr" จะถูกปล่อยออกจากทั้งสองด้านของสมการที่กำหนด
สิ่งสุดท้ายสิ่งหนึ่ง: ตรวจสอบเคส (array == null) ในการออกแบบที่ดีกว่าการเขียนโปรแกรมภาษา แต่แม้ในภาษาออกแบบมาไม่ดีกับบางวินัยหนึ่งขั้นพื้นฐานค่อนข้างใช้เลือกประเภทที่จะแสดงความไม่ใช่ตัวตน นี่ไม่ได้เกี่ยวอะไรกับการเขียนโปรแกรมฟังก์ชั่นซึ่งเป็นคำถามเกี่ยวกับท้ายที่สุดดังนั้นฉันไม่ได้จัดการกับมัน
break
และreturn answer
สามารถถูกแทนที่ด้วยreturn
วงใน ใน FP คุณสามารถใช้ผลตอบแทนก่อนกำหนดได้โดยใช้การต่อเนื่องดูที่en.wikipedia.org/wiki/Continuation