Coroutine vs Continuation vs Generator


147

ความแตกต่างระหว่าง coroutine และความต่อเนื่องและกำเนิดคืออะไร?


2
ฉันสงสัยว่า coroutines และ continuations จะเทียบเท่าได้อย่างมีประสิทธิภาพ ฉันรู้ว่ามันเป็นไปได้ที่จะสร้างแบบจำลอง coroutines ด้วยการต่อเนื่อง แต่เป็นไปได้หรือไม่ที่จะสร้างแบบจำลองการต่อเนื่องด้วย coroutines หรือไม่เพราะการต่อเนื่องนั้นมีประสิทธิภาพมากกว่า
nalply

คำตอบ:


127

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

เครื่องกำเนิดไฟฟ้าเป็นหลัก coroutine ลดลง (ไม่สมมาตร) ความแตกต่างระหว่าง coroutine และตัวกำเนิดคือ coroutine สามารถยอมรับการขัดแย้งหลังจากที่ถูกเรียกในตอนแรกในขณะที่ตัวสร้างไม่สามารถ

มันค่อนข้างยากที่จะเกิดขึ้นกับตัวอย่างเล็ก ๆ น้อย ๆ ที่คุณจะใช้ coroutines แต่นี่คือความพยายามที่ดีที่สุดของฉัน รับรหัส Python นี้ (ทำขึ้น) เป็นตัวอย่าง

def my_coroutine_body(*args):
    while True:
        # Do some funky stuff
        *args = yield value_im_returning
        # Do some more funky stuff

my_coro = make_coroutine(my_coroutine_body)

x = 0
while True:
   # The coroutine does some funky stuff to x, and returns a new value.
   x = my_coro(x)
   print x

ตัวอย่างของการใช้ coroutines คือ lexers และ parsers หากไม่มี coroutines ในภาษาหรือเลียนแบบโค้ดเลเยอร์และการแยกวิเคราะห์จะต้องผสมเข้าด้วยกันแม้ว่าจะเป็นข้อกังวลสองประการก็ตาม แต่ใช้ coroutine คุณสามารถแยก lexing และการแยกรหัส

(ฉันจะแปรงมากกว่าความแตกต่างระหว่าง coroutines แบบสมมาตรและแบบอสมมาตรพอเพียงเพื่อบอกว่ามันเท่ากันคุณสามารถแปลงจากแบบหนึ่งไปอีกแบบหนึ่งได้ ง่ายต่อการเข้าใจฉันกำลังสรุปว่าจะใช้ Coroutines แบบอสมมาตรใน Python ได้อย่างไร)

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

สมมติว่า Python มีฟังก์ชั่นที่เรียกว่าcallcc()และฟังก์ชั่นนี้ใช้สองอาร์กิวเมนต์แรกคือฟังก์ชันและที่สองเป็นรายการอาร์กิวเมนต์ที่จะเรียกใช้ ข้อ จำกัด เพียงอย่างเดียวในฟังก์ชั่นนั้นก็คือว่าอาร์กิวเมนต์สุดท้ายที่ใช้จะเป็นฟังก์ชัน (ซึ่งจะเป็นความต่อเนื่องในปัจจุบันของเรา)

def foo(x, y, cc):
   cc(max(x, y))

biggest = callcc(foo, [23, 42])
print biggest

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

'42'ผลจากการทั้งหมดนี้จะเป็นไปได้ว่าตัวแปรหลามสมมุติเราจะพิมพ์

ฉันหวังว่าจะช่วยได้และฉันมั่นใจว่าคำอธิบายของฉันสามารถปรับปรุงได้ในไม่ช้า!


6
หนึ่ง nit: continuations ที่มีการคั่นคือฟังก์ชั่น แต่การเชื่อมต่อที่ไม่ จำกัด นั้นไม่ได้: okmij.org/ftp/continuations/undelimited.html#delim-vs-undelim
Frank Shearar

2
นั่นเป็นจุดที่ดี ที่กล่าวไว้ในแอปพลิเคชันที่ใช้งานได้จริงที่สุดเมื่อผู้คนพูดว่า 'ความต่อเนื่อง' พวกเขากำลังพูดถึงความต่อเนื่องบางส่วน / ที่คั่น การนำสิ่งต่อเนื่องชนิดอื่นมาใช้จะทำให้เกิดความสับสนในคำอธิบายบ้าง
Keith Gaughan

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

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

2
ฉันไม่ได้ไปงานปาร์ตี้เพราะนี่เป็นผลลัพธ์แรกที่ฉันได้รับจาก google เมื่อฉันค้นหา "coroutine vs generator" ฉันหวังว่าจะได้พบข้อมูลที่ดีเกี่ยวกับความแตกต่างของพวกเขา อย่างไรก็ตามฉันพบมันที่อื่น และฉันไม่ใช่คนแรกที่ชี้ให้เห็นว่าคำอธิบายของคุณเกี่ยวกับการต่อเนื่องนั้นผิด ปัญหาคือใครบางคนจะเข้าใจผิดและอาจสับสนในภายหลังเมื่อเธอหรือเขาพบคำเดียวกันกับที่ใช้สำหรับสิ่งที่แตกต่าง
Ivancho

33

Coroutine เป็นหนึ่งในหลาย ๆ โพรซีเดอร์ที่ผลัดกันทำงานแล้วหยุดเพื่อควบคุม Coroutines อื่น ๆ ในกลุ่ม

ความต่อเนื่องเป็น "ตัวชี้ไปยังฟังก์ชั่น" ที่คุณส่งไปยังบางโพรซีเดอร์ที่จะดำเนินการ ("ดำเนินการต่อด้วย") เมื่อทำโพรซีเดอร์นั้น

ตัวสร้าง (ใน. NET) เป็นโครงสร้างภาษาที่สามารถคายออกค่า "หยุด" การดำเนินการของวิธีการแล้วดำเนินการต่อจากจุดเดียวกันเมื่อถูกถามถึงค่าถัดไป


ฉันรู้ว่าคำตอบอาจไม่ถูกต้อง แต่ในคำถามระดับนี้ฉันพยายามทำให้มันง่าย นอกจากนี้ผมไม่เข้าใจจริงๆทั้งหมดนี้เอง :)
zvolkov

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

2
การแก้ไขเล็กน้อย: "... รวมถึง call stack และตัวแปรทั้งหมด แต่ไม่ใช่ค่า" (หรือเพียงแค่วาง "ตัวแปรทั้งหมด") การทำต่อเนื่องจะไม่เก็บค่าไว้ แต่มีเพียงการเรียกสแต็ก
nalply

ไม่การต่อเนื่องไม่ใช่ "ตัวชี้ไปยังฟังก์ชัน" ในการใช้งานที่ไร้เดียงสาที่สุดนั้นมีตัวชี้ไปยังฟังก์ชั่นและสภาพแวดล้อมที่เก็บตัวแปรท้องถิ่น และจะไม่ส่งคืนเว้นแต่คุณจะใช้การโทร / cc เพื่อจับภาพด้วยค่าที่ส่งคืน
NalaGinrut

9

ในเวอร์ชันที่ใหม่กว่าของ Python คุณสามารถส่งค่าไปยังเครื่องกำเนิดไฟฟ้าด้วยgenerator.send()ซึ่งทำให้เครื่องกำเนิดงูหลามมีประสิทธิภาพ

ความแตกต่างที่สำคัญระหว่าง python Generator และตัวสร้างอื่น ๆ คือ greenlet นั่นคือใน python คุณyield valueสามารถกลับไปที่ caller ได้เท่านั้น ในขณะที่อยู่ในกรีนเล็ตtarget.switch(value)คุณสามารถพาคุณไปยังCoroutine เป้าหมายที่เฉพาะเจาะจงและให้ค่าที่targetต้องการให้ทำงานต่อไป


3
แต่ใน Python การyieldโทรทั้งหมดจะต้องอยู่ในฟังก์ชันเดียวกันซึ่งเรียกว่า "เครื่องกำเนิด" คุณไม่สามารถyieldจากย่อยฟังก์ชั่นซึ่งเป็นสาเหตุของงูหลามจะเรียกว่ากึ่ง coroutines-ขณะ Lua มีcoroutines ไม่สมมาตร (มีข้อเสนอที่จะเผยแพร่ผลผลิต แต่ฉันคิดว่าคนเหล่านั้นเพียงโคลน)
cdunn2001

7
@ cdunn2001: (ความคิดเห็นโดย Winston) Python3.3 นำเสนอนิพจน์ "yield from" ซึ่งให้คุณได้ผลลัพธ์จาก sub-generator
Linus Caldwell
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.