ทำไมการใช้ตัวดำเนินการที่ได้รับมอบหมายหรือลูปหมดกำลังใจในการเขียนโปรแกรมใช้งานได้?


9

หากฟังก์ชั่นของฉันมีคุณสมบัติตรงตามข้อกำหนดสองข้อข้างล่างฉันเชื่อว่าฟังก์ชั่นSum จะคืนค่าการสรุปของรายการในรายการที่รายการประเมินเป็นจริงสำหรับเงื่อนไขที่กำหนดมีคุณสมบัติเหมาะสมที่จะเรียกว่าเป็นฟังก์ชันบริสุทธิ์ใช่ไหม

1) สำหรับชุด i / p ที่กำหนดจะส่งคืน o / p เดียวกันโดยไม่คำนึงถึงเวลาที่เรียกใช้ฟังก์ชัน

2) มันไม่มีผลข้างเคียงใด ๆ

public int Sum(Func<int,bool> predicate, IEnumerable<int> numbers){
    int result = 0;
    foreach(var item in numbers)
        if(predicate(item)) result += item;
    return result;
}

ตัวอย่าง: Sum(x=>x%2==0, new List<int> {1,2,3,4,5...100});

เหตุผลที่ฉันถามคำถามนี้เพราะฉันเห็นเกือบทุกที่ที่ผู้คนแนะนำให้หลีกเลี่ยงการมอบหมายและลูปเพราะมันเป็นรูปแบบการเขียนโปรแกรมที่จำเป็น ดังนั้นสิ่งที่สามารถไปผิดกับตัวอย่างข้างต้นซึ่งทำให้การใช้ลูปและผู้ประกอบการที่ได้รับมอบหมายในบริบทของการเขียนโปรแกรมฟังก์ชั่น?


1
มันไม่มีผลข้างเคียงใด ๆ - มันมีผลข้างเคียงเมื่อitemตัวแปรกลายพันธุ์ในลูป
Fabio

@Fabio โอเค แต่คุณสามารถอธิบายเพิ่มเติมเกี่ยวกับขอบเขตของผลข้างเคียงได้หรือไม่?
rahulaga_dev

คำตอบ:


16

มันคืออะไรในการเขียนโปรแกรมฟังก์ชั่นที่สร้างความแตกต่าง?

ฟังก์ชั่นการเขียนโปรแกรมโดยหลักการ ที่เปิดเผย คุณพูดในสิ่งที่ผลลัพธ์ของคุณแทนที่จะเป็นวิธีการคำนวณ

ลองมาดูตัวอย่างการใช้งานจริง ๆ ในการใช้งานของคุณ ใน Haskell มันจะเป็น:

predsum pred numbers = sum (filter pred numbers)

มันชัดเจน หรือไม่ว่าผลลัพธ์คืออะไร ค่อนข้างจะเป็นผลรวมของตัวเลขที่ตรงกับภาคแสดง วิธีการก็คือการคำนวณ? ฉันไม่สนใจถามผู้แปล

คุณอาจบอกได้ว่าการใช้sumและfilterเป็นกลอุบายและไม่นับ ให้นำไปใช้โดยไม่ต้องมีผู้ช่วยเหลือเหล่านี้ (แม้ว่าวิธีที่ดีที่สุดคือการใช้พวกมันก่อน)

วิธีแก้ปัญหา "ฟังก์ชั่นการเขียนโปรแกรม 101" ที่ไม่ได้ใช้งานsumคือการเรียกซ้ำ:

sum pred list = 
    case list of
        [] -> 0
        h:t -> if pred h then h + sum pred t
                         else sum pred t

มันยังค่อนข้างชัดเจนว่าผลลัพธ์ในแง่ของการเรียกใช้ฟังก์ชันเดียวคืออะไร มันเป็นอย่างใดอย่างหนึ่ง0หรือขึ้นอยู่กับrecursive call + h or 0 pred hยังคง straighforward สวยแม้ว่าผลลัพธ์ที่ได้คือไม่ชัดเจนทันที (แม้ว่าจะมีนิด ๆ หน่อย ๆ ของการปฏิบัตินี้จริงๆอ่านเช่นเดียวกับforวง)

เปรียบเทียบกับรุ่นของคุณ:

public int Sum(Func<int,bool> predicate, IEnumerable<int> numbers){
    int result = 0;
    foreach(var item in numbers)
        if (predicate(item)) result += item;
    return result;
}

ผลลัพธ์คืออะไร โอ้ฉันเข้าใจแล้ว: โสดreturnreturn resultคำสั่งไม่น่าประหลาดใจที่นี่:

แต่สิ่งที่เป็นresult?int result = 0? ดูไม่ถูกต้อง 0คุณทำบางสิ่งบางอย่างในภายหลังด้วยที่ ตกลงคุณเพิ่มitemของมัน และอื่น ๆ

แน่นอนว่าสำหรับโปรแกรมเมอร์ส่วนใหญ่แล้วมันค่อนข้างชัดเจนว่าเกิดอะไรขึ้นใน funciton ง่าย ๆ แบบนี้ แต่เพิ่มreturnข้อความพิเศษหรือมากกว่านั้นและมันก็ยากที่จะติดตาม รหัสทั้งหมดเกี่ยวกับวิธีการและสิ่งที่เหลือสำหรับผู้อ่านที่จะคิดออก -นี้เป็นที่ชัดเจนสไตล์ความจำเป็นมาก

ดังนั้นตัวแปรและลูปผิดหรือเปล่า?

เลขที่

มีหลายสิ่งหลายอย่างที่อธิบายได้ง่ายกว่าพวกเขาและอัลกอริธึมหลายอย่างที่ต้องใช้สถานะที่เปลี่ยนแปลงได้นั้นรวดเร็ว แต่ตัวแปรมีความจำเป็นโดยเนื้อแท้อธิบายว่าแทนที่จะเป็นอะไรและให้การคาดการณ์เล็กน้อยว่าค่าของพวกเขาอาจเป็นไม่กี่บรรทัดในภายหลังหรือหลังจากการวนซ้ำซ้ำสองสามครั้ง โดยทั่วไปแล้วลูปต้องการสถานะที่จะทำให้เข้าใจได้และดังนั้นจึงจำเป็นอย่างยิ่งเช่นกัน

ตัวแปรและลูปไม่ใช่การเขียนโปรแกรมที่ใช้งานได้

สรุป

การเขียนโปรแกรม funcitonal ร่วมสมัยเป็นรูปแบบและวิธีคิดที่มีประโยชน์มากกว่ากระบวนทัศน์ การตั้งค่าที่แข็งแกร่งสำหรับฟังก์ชั่นบริสุทธิ์อยู่ในความคิดนี้ แต่มันเป็นเพียงส่วนเล็ก ๆ

ภาษาที่แพร่หลายส่วนใหญ่อนุญาตให้คุณใช้โครงสร้างที่ใช้งานได้บางอย่าง ตัวอย่างเช่นใน Python คุณสามารถเลือกระหว่าง:

result = 0
for num in numbers:
    if pred(result):
        result += num
return result

หรือ

return sum(filter(pred, numbers))

หรือ

return sum(n for n in numbers if pred(n))

นิพจน์การทำงานเหล่านี้เหมาะสมอย่างยิ่งสำหรับปัญหาประเภทนั้นและทำให้โค้ดสั้นลง (และสั้นลงก็ดี ) คุณไม่ควรแทนที่รหัสที่จำเป็นด้วยความคิดโดยไม่ตั้งใจ แต่เมื่อมันเข้ากันแล้วพวกมันก็เป็นตัวเลือกที่ดีกว่า


ขอบคุณสำหรับคำอธิบายที่ดี !!
rahulaga_dev

1
@RahulAgarwal คุณอาจพบว่าคำตอบนี้น่าสนใจมันรวบรวมแนวคิดเกี่ยวกับการสร้างสัจธรรมกับการอธิบายขั้นตอนต่างๆ ฉันชอบวลีที่ว่า "ภาษาที่ประกาศมีผลข้างเคียงในขณะที่ภาษาที่ไม่จำเป็น" - โดยทั่วไปโปรแกรมการทำงานจะมีการตัดที่ชัดเจนและชัดเจนมากระหว่างโค้ดที่เกี่ยวข้องกับโลกภายนอก (หรือดำเนินการอัลกอริทึมที่เหมาะสม)
Frax

1
@Frax: ขอบคุณ !! ฉันจะดูมัน เมื่อเร็ว ๆ นี้ยังได้พบกับ Rich Hickey พูดคุยเกี่ยวกับค่านิยมมันยอดเยี่ยมจริงๆ ฉันคิดว่ากฎง่ายๆ - "ทำงานกับค่านิยมและการแสดงออกมากกว่าการทำงานกับสิ่งที่ถือค่าและสามารถเปลี่ยน"
rahulaga_dev

1
@ แฟร็กซ์: มันก็ยุติธรรมที่จะพูดว่า FP นั้นเป็นนามธรรมมากกว่าการเขียนโปรแกรมที่จำเป็น - เพราะท้ายที่สุดแล้วบางคนต้องสั่งเครื่องเกี่ยวกับ "วิธีการทำ" ใช่ไหม? ถ้าใช่แสดงว่าโปรแกรมที่ไม่มีความจำเป็นมีการควบคุมระดับต่ำมากเมื่อเทียบกับ FP?
rahulaga_dev

1
@ แฟร็กซ์: ฉันเห็นด้วยกับราหุลว่าความจำเป็นอยู่ในระดับที่ต่ำกว่าในแง่ที่ว่ามันใกล้เคียงกับเครื่องจักรพื้นฐาน หากฮาร์ดแวร์สามารถทำสำเนาข้อมูลได้โดยไม่ต้องเสียค่าใช้จ่ายเราจะไม่จำเป็นต้องทำการอัพเดทแบบทำลายเพื่อปรับปรุงประสิทธิภาพ ในแง่นี้กระบวนทัศน์ที่จำเป็นนั้นอยู่ใกล้กับโลหะมากขึ้น
Giorgio

9

การใช้สถานะที่เปลี่ยนแปลงไม่ได้โดยทั่วไปจะไม่ได้รับการสนับสนุนในการตั้งโปรแกรมการทำงาน เนื่องจากการวนซ้ำนั้นมีประโยชน์เฉพาะเมื่อรวมกับสถานะที่ไม่แน่นอน

ฟังก์ชั่นโดยรวมนั้นบริสุทธิ์ซึ่งยอดเยี่ยม แต่กระบวนทัศน์ของการเขียนโปรแกรมฟังก์ชั่นไม่เพียง แต่นำมาใช้ในระดับฟังก์ชั่นทั้งหมดเท่านั้น นอกจากนี้คุณยังต้องการหลีกเลี่ยงสถานะที่ไม่แน่นอนในระดับท้องถิ่นและภายในฟังก์ชัน และการใช้เหตุผลนั้นเหมือนกัน: การหลีกเลี่ยงสถานะที่ไม่แน่นอนทำให้รหัสเข้าใจง่ายขึ้นและป้องกันข้อผิดพลาดบางอย่าง

ในกรณีของคุณคุณสามารถเขียนnumbers.Where(predicate).Sum()ที่ชัดเจนง่ายกว่ามาก และง่ายขึ้นหมายถึงข้อบกพร่องน้อยลง


ขอบคุณ !! ฉันคิดว่าฉันขาดเส้นที่โดดเด่น - แต่กระบวนทัศน์การเขียนโปรแกรมเชิงฟังก์ชั่นไม่เพียงแค่นำมาใช้ในระดับฟังก์ชั่นทั้งหมดแต่ตอนนี้ฉันยังสงสัยว่าจะเห็นขอบเขตของภาพนี้ได้อย่างไร โดยพื้นฐานจากมุมมองของผู้บริโภคมันเป็นฟังก์ชั่นที่บริสุทธิ์ แต่นักพัฒนาที่เขียนฟังก์ชั่นนี้ไม่ได้ปฏิบัติตามแนวทางฟังก์ชั่นที่บริสุทธิ์? สับสน :(
rahulaga_dev

@RahulAgarwal: ขอบเขตอะไร
JacquesB

ฉันสับสนในกรณีที่กระบวนทัศน์การเขียนโปรแกรมมีคุณสมบัติที่จะเป็น FP จากผู้บริโภคในมุมมองของฟังก์ชั่น? Bcoz ถ้าฉันดูการใช้งานของWherein numbers.Where(predicate).Sum()- มันใช้ประโยชน์จากforeachลูป
rahulaga_dev

3
@RahulAgarwal: ในฐานะผู้บริโภคของฟังก์ชั่นคุณไม่สนใจหรอกว่าฟังก์ชั่นหรือโมดูลจะใช้สถานะที่ไม่แน่นอนภายในถ้ามันเป็นความบริสุทธิ์จากภายนอก
JacquesB

7

ในขณะที่คุณถูกต้องว่าจากมุมมองของผู้สังเกตการณ์ภายนอกSumฟังก์ชั่นของคุณบริสุทธิ์การใช้งานภายในไม่ชัดเจน - คุณมีสถานะที่เก็บไว้resultซึ่งคุณกลายพันธุ์ซ้ำ ๆ หนึ่งในเหตุผลที่จะหลีกเลี่ยงการรัฐไม่แน่นอนเป็นเพราะมันก่อให้เกิดภาระกับความรู้ความเข้าใจมากขึ้นเกี่ยวกับการเขียนโปรแกรมซึ่งนำไปสู่การเปิดข้อบกพร่องมากขึ้น[อ้างจำเป็น]

ในตัวอย่างง่ายๆเช่นนี้จำนวนของสถานะที่ไม่แน่นอนที่ถูกจัดเก็บอาจมีขนาดเล็กพอที่จะไม่ทำให้เกิดปัญหาร้ายแรงใด ๆ หลักการทั่วไปยังคงใช้ ตัวอย่างของของเล่นSumอาจไม่ใช่วิธีที่ดีที่สุดในการแสดงให้เห็นถึงประโยชน์ของการเขียนโปรแกรมการทำงานเหนือสิ่งจำเป็น - ลองทำบางสิ่งด้วยสถานะที่ไม่แน่นอนจำนวนมากและข้อดีอาจชัดเจนขึ้น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.