Qual é a diferença entre uma corotina e uma continuação e um gerador?
Qual é a diferença entre uma corotina e uma continuação e um gerador?
Respostas:
Vou começar com os geradores, pois são o caso mais simples. Como @zvolkov mencionou, são funções / objetos que podem ser chamados repetidamente sem retornar, mas quando chamados retornarão (produzirão) um valor e suspenderão sua execução. Quando eles são chamados novamente, eles iniciam a partir do ponto em que suspenderam a execução e fazem suas ações novamente.
Um gerador é essencialmente uma corotina cortada (assimétrica). A diferença entre uma corotina e um gerador é que uma corotina pode aceitar argumentos depois de ter sido chamada inicialmente, enquanto um gerador não pode.
É um pouco difícil criar um exemplo trivial de onde você usaria corotinas, mas aqui está minha melhor tentativa. Tome esse código Python (composto) como exemplo.
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
Um exemplo de onde as corotinas são usadas é lexers e analisadores. Sem corotinas no idioma ou emuladas de alguma forma, o código lexing e de análise precisa ser misturado, mesmo que sejam realmente duas preocupações separadas. Mas, usando uma corotina, é possível separar o código lexing e de análise.
(Vou abordar a diferença entre corotinas simétricas e assimétricas. Basta dizer que elas são equivalentes, você pode converter de uma para a outra, e as corotinas assimétricas - que são as mais parecidas com geradores - são as Eu estava descrevendo como é possível implementar coroutines assimétricas em Python.)
Continuações são realmente bestas muito simples. São funções representando outro ponto do programa que, se você o chamar, fará com que a execução mude automaticamente para o ponto que a função representa. Você usa versões muito restritas deles todos os dias sem nem perceber. Exceções, por exemplo, podem ser pensadas como uma espécie de continuação de dentro para fora. Vou dar um exemplo de pseudocódigo baseado em Python de uma continuação.
Digamos que o Python tenha uma função chamada callcc()
, e essa função tenha dois argumentos, o primeiro sendo uma função e o segundo uma lista de argumentos para chamá-lo. A única restrição a essa função seria que o último argumento necessário será uma função (que será nossa continuação atual).
def foo(x, y, cc):
cc(max(x, y))
biggest = callcc(foo, [23, 42])
print biggest
O que aconteceria é que callcc()
, por sua vez, chamaria foo()
a continuação atual ( cc
), isto é, uma referência ao ponto no programa em que callcc()
foi chamado. Quando foo()
chama a continuação atual, é essencialmente o mesmo que dizer callcc()
para retornar com o valor com o qual você está chamando a continuação atual e, quando faz isso, reverte a pilha para onde a continuação atual foi criada, ou seja, quando você chamou callcc()
.
O resultado de tudo isso seria que nossa hipotética variante Python seria impressa '42'
.
Espero que ajude, e tenho certeza que minha explicação pode melhorar um pouco!
A corotina é um dos vários procedimentos que se revezam no trabalho e depois param para dar controle às outras corotinas do grupo.
A continuação é um "ponteiro para uma função" que você passa para algum procedimento, a ser executado ("continuado com") quando esse procedimento é concluído.
Generator (no .NET) é uma construção de linguagem que pode cuspir um valor, "pausar" a execução do método e prosseguir a partir do mesmo ponto quando solicitado pelo próximo valor.
Na versão mais recente do Python, você pode enviar valores para os Geradores com generator.send()
, o que faz com que os Geradores de python efetivamente sejam rotineiros.
A principal diferença entre o python Generator e outro gerador, digamos greenlet, é que, em python, você yield value
só pode retornar ao chamador. Enquanto estiver no greenlet, target.switch(value)
você pode levá-lo a uma corotina de destino específica e gerar um valor em que o valor target
continuaria a ser executado.
yield
chamadas devem estar na mesma função, chamada de "Gerador". Você não pode yield
de uma sub-função, e é por isso que os Python são chamados de semi-corotinas , enquanto Lua possui corotinas assimétricas . (Existem propostas para propagar os rendimentos, mas acho que só esses turvar as águas.)