Pular para o conteúdo

Pipelines

O operador >> com sift, morph e distill — transformação de dados como sintaxe.

O operador >>#

Pipelines não são uma biblioteca: >>, sift, morph e distill fazem parte da gramática da linguagem.

dataforge
nums := [1, 2, 3, 4, 5, 6]

out nums >> sift n: n % 2 is 0        # [2, 4, 6]      — filtra
out nums >> morph n: n * n            # [1, 4, ...]    — transforma
out nums >> distill acc, v: acc + v 0 # 21             — reduz

sift — filtrar#

dataforge
pessoas := [
    {"nome": "Ana", "idade": 30},
    {"nome": "Bruno", "idade": 17},
    {"nome": "Carla", "idade": 45}
]

adultos := pessoas >> sift p: p["idade"] bigger_eq 18
out adultos >> morph p: p["nome"]
saída
[Ana, Carla]

morph — transformar#

dataforge
precos := [100, 200, 300]
out precos >> morph p: round(p * 1.15, 2)

nomes := ["ana", "bruno"]
out nomes >> morph n: n.capitalize()

distill — reduzir#

A forma é distill acumulador, valor: expressão valor_inicial:

dataforge
nums := [1, 2, 3, 4, 5]

out nums >> distill acc, v: acc + v 0      # 15  — soma
out nums >> distill acc, v: acc * v 1      # 120 — produto

palavras := ["Data", "Forge"]
out palavras >> distill acc, v: acc + v ""  # DataForge

Sem valor inicial, distill usa o primeiro elemento como acumulador.

Encadeando#

dataforge
vendas := [120, 45, 300, 80, 500, 15, 250]

total := vendas
    >> sift v: v bigger_eq 100
    >> morph v: v * 1.1
    >> distill acc, v: acc + v 0

out $"total das vendas grandes com 10%: {round(total, 2)}"

Uma linha que começa com >> continua a expressão anterior. Isso permite quebrar pipelines longos sem ruído.

Com ações nomeadas#

Em vez da lambda inline, você pode passar o nome de uma ação:

dataforge
action eh_primo(n):
    given n smaller 2:
        yield no
    i := 2
    persist i * i smaller_eq n:
        given n % i is 0:
            yield no
        i += 1
    yield yes

action ao_quadrado(n):
    yield n * n

nums := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
out nums >> sift eh_primo                       # [2, 3, 5, 7, 11]
out nums >> sift eh_primo >> morph ao_quadrado  # [4, 9, 25, 49, 121]

A ação nomeada é preferível quando a lógica tem mais de uma linha, ou quando ela é reutilizada — e ainda pode ser testada isoladamente.

Pipeline, compreensão ou laço?#

As três formas resolvem o mesmo problema. A escolha é de legibilidade:

dataforge
# pipeline — vários estágios encadeados
nums >> sift n: n % 2 is 1 >> morph n: n * 2

# compreensão — um filtro e uma transformação
[n * 2 cycle n in nums given n % 2 is 1]

# métodos — quando já se tem a lista na mão
nums.filter(lambda n: n % 2 is 1).map(lambda n: n * 2)
SituaçãoPrefira
um filtro e uma transformaçãocompreensão
três ou mais estágiospipeline
termina com uma reduçãopipeline (distill)
duas fontes combinadascompreensão
lógica com vários passos ou efeitoslaço cycle

Um relatório completo#

dataforge
vendas := [
    {"vendedor": "Ana", "valor": 1200},
    {"vendedor": "Bruno", "valor": 800},
    {"vendedor": "Ana", "valor": 300},
    {"vendedor": "Carla", "valor": 2000}
]

valores := vendas >> morph v: v["valor"]

out $"faturamento:  {sum(valores)}"
out $"ticket medio: {round(mean(valores), 2)}"
out $"acima de 1000: {vendas >> sift v: v["valor"] bigger 1000 >> morph v: v["vendedor"]}"
saída
faturamento:  4300
ticket medio: 1075.0
acima de 1000: [Ana, Carla]

Com streams#

Pipelines operam sobre listas em memória. Para fonte infinita ou arquivo grande, use generators — que avaliam sob demanda e depois entregam a lista ao pipeline:

dataforge
stream action naturais():
    n := 1
    persist yes:
        emit n
        n += 1

primeiros := naturais().take(20)
out primeiros >> sift n: n % 3 is 0