Coroutine проти продовження проти генератора


147

Яка різниця між співпрограмою та продовженням та генератором?


2
Цікаво, чи співпраці та продовження дійсно рівнозначні. Я знаю, що можна моделювати супроводи з продовженнями, але чи можна моделювати продовження за допомогою супротивників чи ні, тому що продовження суворо потужніше?
напів

Відповіді:


127

Почну з генераторів, побачивши, як це найпростіший випадок. Як зазначав @zvolkov, вони є функціями / об'єктами, які можна повторно викликати без повернення, але при виклику повернуть (дадуть) значення і призупинять їх виконання. Коли їх знову зателефонують, вони почнуть виходити з місця, коли вони останнім часом призупинили виконання, і робитимуть свою справу знову.

Генератор - це по суті скорочена (асиметрична) коренева програма. Різниця між програмою та генератором полягає в тому, що програма може приймати аргументи після її первинного виклику, тоді як генератор не може.

Трохи важко придумати банальний приклад того, де ти б використовував супроводи, але ось моя найкраща спроба. Візьмемо цей (складений) код 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

Прикладом використання супротинів є лексери та аналізатори. Без супротивної мови в мові або імітації якось, лексичний і синтаксичний код потрібно змішувати разом, хоча вони справді є двома окремими проблемами. Але, використовуючи супровід, ви можете відокремити код лексики та розбору.

(Я збираюся вирішити різницю між симетричними та асиметричними корументами. Досить сказати, що вони еквівалентні, ви можете конвертувати з однієї в іншу, а асиметричні функції, що найбільше схожі на генератори, - це простіше зрозуміти. Я окреслював, як можна реалізувати асиметричні супроводи в Python.)

Продовження насправді досить прості звірі. Все, що вони є, - це функції, що представляють іншу точку програми, яка, якщо ви її зателефонуєте, призведе до автоматичного переходу в точку, яку представляє функція. Ви використовуєте дуже обмежені версії їх щодня, навіть не усвідомлюючи цього. Наприклад, винятки можна розглядати як певне продовження зсередини. Я наведу вам приклад продовження псевдокоду на основі 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() .

Результатом всього цього став би те, що наш гіпотетичний варіант Python надрукував би '42' .

Я сподіваюся, що це допомагає, і я впевнений, що моє пояснення можна значно покращити!


6
Один NIT: роздільники продовжень є функціями, але неделімітованій продовження не є: okmij.org/ftp/continuations/undelimited.html#delim-vs-undelim
Frank Shearar

2
Це хороший момент. Це говорить у більшості практичних застосувань, коли люди кажуть «продовження», вони говорять про часткове / обмежене продовження. Приведення в різні види продовження дещо затуманило б пояснення.
Кіт Гоген

1
Продовження не є функціями, хоча їх можна переробити у функції. "Це зазначає, що в більшості практичних застосувань, коли люди кажуть" продовження ", вони говорять про часткове / обмежене продовження". Чи вказуєте ви на таке вживання терміна "продовження"? Я ніколи не зустрічав такого використання. Також ви навели приклад для необов’язаного продовження, використовуючи call / cc. Оператори з обмеженим продовженням зазвичай "скидаються" і "зміщуються" (вони можуть мати інші назви).
Іванчо

3
Почнемо з того, що минуло п’ять років, як я це написав. Ти дещо спізнився на вечірку. По-друге, я знаю, що небажане продовження не є функцією, але ви намагаєтесь пояснити, як вони працюють, не звертаючись до них як таких, одночасно зберігаючи мову прямо. З точки зору пересічного програміста, той факт, що невдалене продовження не повертається, просто робить його однократною функцією, що не відповідає правильному визначенню, що таке функція, але це принаймні зрозуміло .
Кіт Гоген

2
Я не запізнююсь на вечірку, оскільки це перший результат, який я отримую в google, коли шукаю "coroutine vs generator". Я сподівався знайти хорошу інформацію про їхні відмінності. У всякому разі, я знайшов це в іншому місці. І я не перший, хто зазначив, що ваше пояснення щодо продовження є неправильним. Проблема полягає в тому, що хтось помилиться і, можливо, буде плутати пізніше, коли вона чи він зустріне те саме слово, яке використовується для чогось іншого.
Іванчо

33

Спрограма - це одна з декількох процедур, яка по черзі виконує свою роботу, а потім робить паузу, щоб дати контроль іншим спільним процедурам у групі.

Продовження - це "вказівник на функцію", яку ви переходите до якоїсь процедури, яка виконується ("продовжується з"), коли ця процедура виконана.

Генератор (в .NET) - це мовна конструкція, яка може виплюнути значення, "призупинити" виконання методу, а потім перейти з тієї ж точки, коли буде запропоновано наступне значення.


Я усвідомлюю, що відповідь може бути не точною, але на цьому рівні запитань я намагався простоти її. До того ж, я сам цього не дуже розумію :)
zvolkov

Генератор в python схожий на версію C #, але реалізується як спеціальний синтаксис для створення екземпляра ітераторного об'єкта, який повертає значення, повернені в наданому вами визначенні "функція".
Бенсон

2
Невелике виправлення: "... включаючи стек виклику та всі змінні, АЛЕ НЕ ЇХ ЦІННОСТІ" (або просто відкиньте "всі змінні"). Продовження не зберігають значення, вони просто містять стек викликів.
напів

Ні, продовження не є "покажчиком на функцію". У найбільш наївній реалізації він містить вказівник на функціонування, а середовище містить локальні змінні. І він ніколи не повертається, якщо ви не використовуєте щось на зразок call / cc для того, щоб зафіксувати його із значенням повернення.
NalaGinrut

9

У більш новій версії Python ви можете надсилати значення Генераторам за допомогою generator.send(), що робить Python Generators ефективно взаємоконтрольним.

Основна відмінність генератора python від іншого генератора, скажімо, greenlet, полягає в тому, що в python ви yield valueможете повернутися лише до абонента. Перебуваючи в greenlet, target.switch(value)може перевезти вас до конкретної цільової програми та отримати значення, де targetби продовжував працювати.


3
Але в Python всі yieldвиклики повинні бути в одній функції, яка називається "Генератор". Ви не можете yieldвикористовувати підфункцію, тому Python називають напівкоротинами , тоді як Lua має асиметричні функції . (Є пропозиції щодо поширення врожаю, але я думаю, що ті лише
каламутять

7
@ cdunn2001: (коментар Вінстона) Python3.3 представив вираз "урожайність", який дозволив отримати вихід з субгенератора.
Лінус Колдвелл
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.