คำอธิบายของ Combinators สำหรับคนทำงาน


93

Combinator คืออะไร ??

เป็น"ฟังก์ชันหรือคำจำกัดความที่ไม่มีตัวแปรอิสระ" (ตามที่กำหนดไว้ใน SO) หรือไม่?

หรือเกี่ยวกับสิ่งนี้: ตามที่John Hughes กล่าวในบทความที่รู้จักกันดีของเขาเรื่อง Arrows "Combinator คือฟังก์ชันที่สร้างส่วนย่อยของโปรแกรมจากส่วนย่อยของโปรแกรม"ซึ่งเป็นข้อได้เปรียบเพราะ "... โปรแกรมเมอร์ที่ใช้ combinators สร้างสิ่งที่ต้องการได้มาก โปรแกรมโดยอัตโนมัติแทนที่จะเขียนทุกรายละเอียดด้วยมือ ". เขากล่าวต่อไปmapและfilterเป็นสองตัวอย่างทั่วไปของตัวผสมดังกล่าว

ตัวผสมบางตัวที่ตรงกับคำจำกัดความแรก:

  • เค
  • คนอื่น ๆ จากTo Mock a Mockingbird (ฉันอาจจะผิด - ฉันยังไม่ได้อ่านหนังสือเล่มนี้)

ตัวผสมบางตัวที่ตรงกับคำจำกัดความที่สอง:

  • แผนที่
  • กรอง
  • พับ / ลด (สันนิษฐานว่า)
  • ใด ๆ ของ >> =, เขียน, fmap ?????

ฉันไม่สนใจคำจำกัดความแรก - สิ่งเหล่านี้ไม่ได้ช่วยฉันในการเขียนโปรแกรมจริง (+1 ถ้าคุณทำให้ฉันเชื่อว่าฉันผิด) โปรดช่วยให้ฉันเข้าใจความหมายที่สอง ฉันคิดว่าแผนที่ตัวกรองและการลดมีประโยชน์: ช่วยให้ฉันเขียนโปรแกรมในระดับที่สูงขึ้น - มีข้อผิดพลาดน้อยลงโค้ดสั้นลงและชัดเจนขึ้น นี่คือคำถามเฉพาะบางส่วนของฉันเกี่ยวกับตัวผสม:

  1. อะไรคือตัวอย่างเพิ่มเติมของตัวผสมเช่นแผนที่ตัวกรอง?
  2. ภาษาโปรแกรมใดที่ตัวรวมกันมักใช้?
  3. ตัวผสมผสานจะช่วยฉันออกแบบ API ให้ดีขึ้นได้อย่างไร
  4. ฉันจะออกแบบ Combinators ที่มีประสิทธิภาพได้อย่างไร
  5. อะไรคือตัวผสมที่คล้ายกับในภาษาที่ไม่ใช้งาน (เช่น Java) หรือภาษาเหล่านี้ใช้อะไรแทนตัวรวมกัน

อัปเดต

ขอบคุณ @CA McCann ตอนนี้ฉันมีความเข้าใจเกี่ยวกับ Combinators มากขึ้น แต่คำถามหนึ่งยังคงเป็นจุดยึดสำหรับฉัน:

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

ฉันสงสัยว่าคำตอบคือรุ่น combinator-heavy นั้นสั้นกว่าชัดเจนกว่าทั่วไปกว่า แต่ฉันจะขอบคุณการอภิปรายในเชิงลึกมากขึ้นถ้าเป็นไปได้

ฉันกำลังมองหาตัวอย่างเพิ่มเติมและคำอธิบายของตัวผสมที่ซับซ้อน (เช่นซับซ้อนกว่าfold) ในภาษาโปรแกรมทั่วไป


4
ฉันพิจารณาแผนที่ / ตัวกรอง / พับฟังก์ชันลำดับที่สูงขึ้นและฉันใช้มันตลอดเวลา ฉันยังไม่รู้ว่าตัวหวีคืออะไร

1
@pst - ฉันจะตอบตกลงเมื่อ 5 วันก่อน แต่ตอนนี้ฉันไม่สามารถเถียงกับ John Hughes ได้
Matt Fenwick

คำตอบ:


93

ฉันไม่สนใจคำจำกัดความแรก - สิ่งเหล่านี้ไม่ได้ช่วยฉันในการเขียนโปรแกรมจริง (+1 ถ้าคุณทำให้ฉันเชื่อว่าฉันผิด) โปรดช่วยฉันเข้าใจนิยามที่สอง ฉันคิดว่าแผนที่ตัวกรองและการลดมีประโยชน์: ช่วยให้ฉันตั้งโปรแกรมในระดับที่สูงขึ้น - มีข้อผิดพลาดน้อยลงโค้ดสั้นลงและชัดเจน

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

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

อะไรคือตัวอย่างเพิ่มเติมของตัวผสมเช่นแผนที่ตัวกรอง?

มีรายการมากเกินไป! ทั้งสองอย่างเปลี่ยนฟังก์ชันที่อธิบายพฤติกรรมของค่าเดียวให้เป็นฟังก์ชันที่อธิบายพฤติกรรมในคอลเล็กชันทั้งหมด คุณยังสามารถมีฟังก์ชันที่แปลงเฉพาะฟังก์ชันอื่น ๆ เช่นการเขียนแบบ end-to-end หรือการแยกและการรวมอาร์กิวเมนต์ใหม่ คุณสามารถมีตัวรวมที่เปลี่ยนการดำเนินการขั้นตอนเดียวเป็นการดำเนินการแบบวนซ้ำที่สร้างหรือใช้คอลเลกชัน หรืออื่น ๆ ทุกชนิดจริงๆ

ภาษาโปรแกรมใดที่ตัวรวมกันมักใช้?

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

ตัวผสมผสานจะช่วยฉันออกแบบ API ให้ดีขึ้นได้อย่างไร

ตามที่คุณกล่าวโดยคิดในแง่ของการปฏิบัติงานระดับสูงและวิธีการโต้ตอบเหล่านั้นแทนที่จะใช้รายละเอียดระดับต่ำ

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

ฉันจะออกแบบ Combinators ที่มีประสิทธิภาพได้อย่างไร

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

อะไรคือตัวผสมที่คล้ายกับในภาษาที่ไม่ใช้งาน (เช่น Java) หรือภาษาเหล่านี้ใช้อะไรแทนตัวรวมกัน

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

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


ตอบโจทย์มาก! มาดูกันว่าฉันเข้าใจหรือไม่ - ตัวผสมไม่ได้พิเศษ แต่เป็นส่วนสำคัญของการสร้างระบบซอฟต์แวร์ที่บำรุงรักษาได้? ฉันยังคงพยายามย่อยคำสั่ง "program fragments" ของ Hughes ในบริบทของโพสต์ของคุณ
Matt Fenwick

@Matt Fenwick: ฉันค่อนข้างแน่ใจว่าเขาใช้ "ส่วนของโปรแกรม" เพื่อหมายถึงประเภทของการเปลี่ยนแปลง / การดำเนินการที่ฉันกำลังพูดถึงโดยเฉพาะอย่างยิ่งถ้าพูดถึง "ลูกศร" ซึ่งเน้นแนวทางนั้นให้ชัดเจนยิ่งขึ้น นอกจากนี้ฉันจะไม่บอกว่ามันเกี่ยวกับการสร้างระบบที่บำรุงรักษาได้อย่างแน่นอน - เพิ่มเติมเกี่ยวกับการสร้างระบบที่มีโมดูลาร์สูงและแยกออกจากกันในลักษณะเฉพาะพร้อมประโยชน์ที่เกิดขึ้น
CA McCann

คุณรู้หรือไม่ว่ามีแหล่งข้อมูลที่ดีสำหรับการอ่านข้อมูลเกี่ยวกับการใช้งานตัวผสมร่วมกัน ขอบคุณมาก!
Matt Fenwick

@ Matt Fenwick: ไม่มีอะไรเฉพาะเจาะจงสำหรับสิ่งนั้นจากด้านบนของหัวของฉันขอโทษ อย่างไรก็ตามมันมีแนวโน้มที่จะเป็นแนวทางที่เป็นธรรมชาติสำหรับโปรแกรมที่เขียนในรูปแบบที่ใช้งานได้จริงดังนั้นเพียงแค่ใช้เวลากับภาษาที่สนับสนุนอย่างมาก (เช่น Haskell หรือ Clojure) และดูว่าโค้ดที่ใช้เขียนในภาษานั้นสะอาดและเป็นสำนวนเพียงใด เป็นวิธีที่ดีในการรับความรู้สึกสไตล์
CA McCann
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.