Uma jóia oculta no Pharo: Generator

Traduzido de A hidden gem in Pharo: Generator.

Julien Delplanque

Julien Delplanque

Follow

Mar 27, 2020 · 3 minutos de leitura

De acordo com a wikipedia:

Um generator é uma rotina especial que pode ser usada para controlar o comportamento de iteração de um loop. — Wikipedia

Encontrei generators pela primeira vez em Python. Fico feliz em saber que o Pharo tem suporte integrado para generators. Esta característica fornecida pela classe Generator parece ser bastante desconhecida pelos Pharoers que conheço, então vamos falar sobre isso.

Seqüência infinita de itens


Romanesco cabbage — Fonte: https://pxhere.com/fr/photo/748888

Com generators, você pode facilmente manipular uma seqüência infinita de itens como um fluxo.

Por exemplo, vamos definir um generator fornecendo uma seqüência infinita de Cores aleatórias:

colorGenerator := Generator on: [ :generator | 
    [ generator yield: Color random ] repeat 
].

Pode ser também [N.T.]:

colorGenerator := Generator on: [ :generator | 
    [ generator nextPut: Color random ] repeat 
].

seguindo mais a interface pública de uma Stream.

Lembrete: Em Pharo a mensagem #repeat enviada a um BlockClosure faz com que o bloco seja chamado um número infinito de vezes (["do something"]repeat é equivalente a [true] whileTrue: ["do something"] ).

Agora que o generator está definido, podemos obter um número arbitrário de cores aleatórias utilizando o método `#next’:

colorGenerator next.  
colorGenerator next.
colorGenerator next.
...

Quando a mensagem “next” é enviada, o cálculo no bloco que define o “Generator” continua até que seja encontrada uma mensagem yield: [N.T.: Ou nextPut:]. A mensagem #yield: pausa o cálculo e retorna o objeto dado como argumento. Este comportamento é repetido cada vez que é enviada a mensagem #next .

Seqüência finita de itens


Fonte: https://pxhere.com/fr/photo/748888

Na verdade, você também pode utilizar “Generators” para gerar uma seqüência finita de itens.

Neste caso, o método #atEnd permite verificar se ainda há itens a serem obtidos do generator. Se #atEnd retornar false, isso significa que não há mais itens a serem obtidos do generator. Assim, chamadas subseqüentes para o #next retornarão nil .

O exemplo a seguir ilustra este comportamento:

finiteNumberGenerator := Generator on: [ :generator |   
    1 to: 3 do: [ :i | generator yield: i 
].
finiteNumberGenerator atEnd >>> false.  
finiteNumberGenerator next  >>> 1.  
finiteNumberGenerator next  >>> 2.  
finiteNumberGenerator next  >>> 3.  
finiteNumberGenerator atEnd >>> true.  
finiteNumberGenerator next  >>> nil

Conclusão

Neste momento você provavelmente estará pensando: “Ok Julien, mas eu posso fazer o mesmo usando o Iterator design pattern.” e você está totalmente certo. Na verdade, um gerador é um iterador! Mas ele tem 3 principais vantagens:

  1. Você não precisa criar uma classe para seu iterator, um generator é como um “iterator anônimo”; e
  2. Você pode escrever um algoritmo gerando uma seqüência infinita de objetos sem ter que se preocupar com o design pattern do iterator, você só precisa utilizar #yield: para retornar os valores gerados.
  3. É memory-efficient quando se trata de uma grande coleção de itens, uma vez que só processará um elemento de cada vez e acionará o cálculo deste elemento lazily, quando for necessário.

Para concluir, os geradores do Pharo fornecem uma maneira prática de lidar com uma seqüência infinita de elementos. Você deve começar a usá-los!

Deixe um comentário