Kodumaro :: Reiteradores em Moonscript

Publicado em 10 de Fevereiro, 2017
As sombras da programação.
MoonScript

Abordarei três formas diferentes de construir reiteradores em Moonscript.

Como Moonscript compila – ou, como tem-se preferido dizer, interpila – para Lua, os princípios básicos aqui usado valem também para Lua.

Reiteradores padrão Lua

A primeira abordagem não usa closures, mas tem algumas restrições. São os reiteradores padrão da linguagem.

O reiterador padrão consiste em três parâmetros: uma função de passo, o estado ou a condição de parada e o valor inicial. A função de passo por sua vez deve receber dois parâmetros, o estado ou a condição de parada e o resultado anterior, e retorna o(s) próximo(s) resultado(s).

Para exemplificar, vamos criar um reiterador que resolva a conjectura de Collatz.

A função de passo deve receber então a condição de parada (sempre 1), o resultado anterior e calcular o próximo elemento da sequência:

_collatz = (stop, last) ->
    return if last == stop
    switch last % 2
        when 0
            last / 2
        when 1
            last * 3 + 1

Quando o estado atinge a condição de parada, retorna nil (nada).

Agora precisamos da função que retornará os três parâmetros para a reiteração:

collatz = (value) -> _collatz, 1, value * 2

Multiplicamos o valor por dois para que o valor inicial também seja retornado na reiteração.

Podemos testar agora nosso reiterador:

mooni> print x for x in collatz 10
10
5
16
8
4
2
1
mooni>

Closures

O problema dos reiteradores padrão de Lua é o transporte de estado de uma execução para outra, que pode se tornar complicado. Podemos resolver isso com closures.

Em Lua/Moonscript, se o for recebe uma função, ele a executa até que o resultado seja nil.

Então a conjectura de Collatz poderia ser resumida em uma única função:

collatz = (value) ->
    value *= 2
    ->
        return if value == 1
        switch value % 2
            when 0
                value =/ 2
            when 1
                value = value * 3 + 1
        value

A função acima se comporta exatamente igual à collatz da abordagem anterior:

mooni> print x for x in collatz 10
10
5
16
8
4
2
1
mooni>

Podemos ousar um pouco mais, por exemplo, calculando a Sequência de Fibonacci, armazendo o estado usando closures:

fib = (n using nil) ->
    a, b, c = 0, 1, 0
    ->
        return if c >= n
        a, b, c = b, a+b, c+1
        a

Testando:

mooni> print x for x in fib 10
1
1
2
3
5
8
13
21
34
55
mooni>

Corrotinas

Podemos também fazer controle de estado sem closures, usando corrotinas.

A lógica é a mesma, porém o código fica ainda mais claro:

fib = (n using coroutine) ->
    import wrap, yield from coroutine
    wrap ->
        a, b = 0, 1
        for _ = 1, n
            a, b = b, a+b
            yield a

Pudemos usar um for para controlar o loop, deixando o código muito mais claro de ser lido e debugado.

Conclusão

É possível usar qualquer uma das três abordagens – e alguma pode ser melhor que as outras, dependendo da situação (a conjectura de Collatz ficou melhor implementada com reiterador padrão, por exemplo). Porém minha recomendação é, na dúvida, preferir corrotinas, pois deixarão seu código mais claro.