Combinators คืออะไรและนำไปใช้กับโครงการการเขียนโปรแกรมอย่างไร (คำอธิบายการปฏิบัติ)


51

Combinators คืออะไร

ฉันกำลังหา:

  • คำอธิบายการปฏิบัติ
  • ตัวอย่างการใช้งาน
  • ตัวอย่างของวิธีการที่ combinators ปรับปรุงคุณภาพ / รหัสทั่วไป

ฉันไม่ได้กำลังมองหา:

  • คำอธิบายของ combinators ที่ไม่ได้ช่วยให้ฉันทำงานเสร็จ (เช่น Y-combinator)

Combinators คล้ายกับ "คำวิเศษณ์" ฟังก์ชั่นที่ใช้ในฟังก์ชั่นแล้วส่งกลับฟังก์ชั่นอื่น ๆ พวกเขาสามารถช่วยลบรหัสที่ซ้ำกันเพราะคุณไม่ต้องการระหว่างตัวแปร สิ่งที่มีประโยชน์บางอย่างคือสองครั้ง (f) = \ x -> f (f (x)), พลิก (op) -> \ xy -> y op x, (.) เช่นเดียวกับใน (fg) x = f (g (x ( )), ($) สามารถช่วยในแผนที่ (เรียกว่า <$> เป็นมัด) เช่นเดียวกับ ($ 5) <$> [(+1), (* 2)] = [6, 10], แกงสามารถใช้ใน Lisp / Python / JavaScript สำหรับแอปพลิเคชั่นบางส่วนและ uncurry สามารถใช้สำหรับฟังก์ชั่นที่ต้องการเรคคอร์ด (tuples) ใน Haskell เมื่อ x |> f = fa, x |> (ความยาว &&& ผลรวม) |> uncurry (/) เป็นค่าเฉลี่ย
aoeu256

คำตอบ:


51

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

ตัวอย่างง่ายๆของ combinator (มีประโยชน์) คือฟังก์ชั่นแลมบ์ดาสองฟังก์ชั่นที่ไม่มีพารามิเตอร์และสร้างฟังก์ชันใหม่ตามลำดับ Combinator ที่แท้จริงมีลักษณะเป็น pseudocode ทั่วไปดังนี้:

func in_sequence(first, second):
  lambda ():
    first()
    second()

สิ่งสำคัญที่ทำให้ combinator นี้เป็นฟังก์ชั่นนิรนาม (ฟังก์ชั่นแลมบ์ดา) ในบรรทัดที่สอง; เมื่อคุณโทร

a = in_sequence(f, g)

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

a() // a is a callable object, i.e. a function without parameters

คุณสามารถมี combinator ที่รันโค้ดบล็อกสองชุดในเวลาเดียวกันในทำนองเดียวกัน:

func in_parallel(first, second):
  lambda ():
    t1 = start_thread(first)
    t2 = start_thread(second)
    wait(t1)
    wait(t2)

แล้วอีกครั้ง

a = in_parallel(f, g)
a()

สิ่งที่เจ๋งคือ 'in_parallel' และ 'in_sequence' เป็นทั้ง combinators ที่มีประเภท / ลายเซ็นเหมือนกันนั่นคือพวกเขาทั้งสองใช้วัตถุฟังก์ชันไร้พารามิเตอร์สองตัวและส่งคืนวัตถุใหม่ จริงๆคุณสามารถเขียนสิ่งต่าง ๆ เช่น

a = in_sequence(in_parallel(f, g), in_parallel(h, i))

และทำงานได้ตามที่คาดไว้

โดยพื้นฐานแล้วผู้ประสานจะอนุญาตให้คุณสร้างโฟลว์การควบคุมของโปรแกรม (เหนือสิ่งอื่นใด) ในขั้นตอนและยืดหยุ่น ตัวอย่างเช่นหากคุณใช้ combinator in_parallel (.. ) เพื่อเรียกใช้ parallelism ในโปรแกรมของคุณคุณสามารถเพิ่มการดีบักที่เกี่ยวข้องกับการใช้งานของ in_parallel combinator ได้ หลังจากนั้นหากคุณสงสัยว่าโปรแกรมของคุณมีข้อผิดพลาดเกี่ยวกับการขนานคุณสามารถนำมาใช้ใหม่ได้ใน in_parallel:

in_parallel(first, second):
  in_sequence(first, second)

และด้วยหนึ่งจังหวะส่วนที่ขนานทั้งหมดได้รับการแปลงเป็นลำดับ!

Combinators มีประโยชน์มากเมื่อใช้งานถูกต้อง

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


9

มันผิดที่แบรนด์ Y-combinator เป็นสิ่งที่ไม่ "ช่วยให้งานเสร็จ" ฉันพบว่ามันมีประโยชน์มากในหลายโอกาส กรณีที่ชัดเจนที่สุดคือเมื่อคุณต้องบู๊ตภาษาที่แปลแล้วแบบฝัง ถ้าคุณให้ชุดที่น้อยที่สุดของวิทยาการคือsequence, select, call, constและclosure allocationมันมีอยู่แล้วเพียงพอสำหรับการสร้างที่สมบูรณ์ภาษาที่ซับซ้อนโดยพลการ ไม่ต้องการการสนับสนุนพิเศษสำหรับการเรียกซ้ำ - สามารถเพิ่มผ่าน combinator จุดคงที่ มิฉะนั้นคุณจะต้องใช้วิธีการที่ซับซ้อนกว่าเดิมมาก

อีกกรณีที่ชัดเจนสำหรับ combinators คือ obfuscation รหัสที่แปลเป็นแคลคูลัส SKI นั้นอ่านไม่ได้ในทางปฏิบัติ หากคุณจริงๆต้องงงงวยการดำเนินการตามขั้นตอนวิธีพิจารณาใช้ combinators, นี่คือตัวอย่าง

และแน่นอนว่า combinators เป็นเครื่องมือสำคัญในการใช้ภาษาที่ใช้งานได้ วิธีที่ง่ายที่สุด (ตามตัวอย่างข้างต้น) คือผ่าน SKI หรือแคลคูลัสที่เทียบเท่า Supercombinatorsถูกนำมาใช้ในการใช้งานอื่น ๆ หนังสือเล่มนี้พูดถึงมันในเชิงลึก

นี่เป็นเรื่องตลกแต่เป็นเรื่องตลกที่ควรค่าแก่การอ่านอย่างละเอียดเนื่องจากมีเทคนิคและทฤษฎีการเขียนโปรแกรมแบบอาร์เคนจำนวนมาก


1
@MattFenwick ความต้องการในการล่ามแบบฝังตัวง่าย ๆ มักจะเกิดขึ้นที่คุณจะไม่คาดหวัง เช่นในกรณีของฉันมันเป็นภาษาที่ฉันต้องออกแบบเพื่อขยายโปรโตคอลการสื่อสาร Simple IPC นั้นไม่เพียงพอดังนั้นจึงต้องมีการเรียกใช้โปรโตคอล
SK-logic

@MattFenwick สำหรับคำถามของคุณ: คุณสามารถลองเขียนโค้ดบางส่วนใน APL หรือ J. Combinators เป็นสิ่งที่จำเป็นดังนั้นคุณจะได้แนวคิดในการใช้อย่างถูกต้อง นอกจากนี้การอ่านในสไตล์ที่ไม่มีจุดอาจช่วยได้: en.wikipedia.org/wiki/Tacit_programming
SK-logic

7

ฉันพบคำถาม StackOverflow คำอธิบายที่ดีของ“ Combinators” (สำหรับผู้ที่ไม่ใช่นักคณิตศาสตร์)นั่นเป็นลูกพี่ลูกน้องของคำถามนี้ หนึ่งในคำตอบที่ชี้ไปยังบล็อกของ Homaliconic ของ Reginald Braithwaiteซึ่งเชื่อมโยงไปยังตัวอย่างที่มีประโยชน์หลายอย่างของ combinators ในรหัส (เช่นK combinator ที่ใช้โดยObject#tapวิธีของ Ruby - อ่านหน้าเพื่อดูตัวอย่างว่าทำไมจึงมีประโยชน์)

หน้าวิกิพีเดียลอจิก Combinatoryอธิบาย combinators มากขึ้นทั่วโลก


ที่อยู่นี้เป็นสัญลักษณ์หัวข้อที่สองของคำถามของฉัน ขอบคุณสำหรับตัวอย่าง!

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