Os transdutores são receitas do que fazer com uma sequência de dados sem saber qual é a sequência subjacente (como fazer). Pode ser qualquer canal seq, assíncrono ou talvez observável.
Eles são composíveis e polimórficos.
A vantagem é que você não precisa implementar todos os combinadores padrão sempre que uma nova fonte de dados é adicionada. De novo e de novo. Como resultado, você, como usuário, pode reutilizar essas receitas em diferentes fontes de dados.
Atualização de anúncio
Antes da versão 1.7 do Clojure, você tinha três maneiras de escrever consultas de fluxo de dados:
- chamadas aninhadas
(reduce + (filter odd? (map #(+ 2 %) (range 0 10))))
- composição funcional
(def xform
(comp
(partial filter odd?)
(partial map #(+ 2 %))))
(reduce + (xform (range 0 10)))
- macro de threading
(defn xform [xs]
(->> xs
(map #(+ 2 %))
(filter odd?)))
(reduce + (xform (range 0 10)))
Com transdutores, você escreverá como:
(def xform
(comp
(map #(+ 2 %))
(filter odd?)))
(transduce xform + (range 0 10))
Todos eles fazem o mesmo. A diferença é que você nunca chama os transdutores diretamente, você os passa para outra função. Os transdutores sabem o que fazer, a função que obtém o transdutor sabe como. A ordem dos combinadores é como você escreve com macro de threading (ordem natural). Agora você pode reutilizar xform
com o canal:
(chan 1 xform)
Os transdutores melhoram a eficiência e permitem que você escreva um código eficiente de uma forma mais modular.
Este é um ensaio decente .
Comparado a compor as chamadas para o antigo
map
,filter
,reduce
etc. você obter um melhor desempenho, porque você não precisa construir coleções intermediárias entre cada passo, e repetidamente andar essas coleções.Comparado a
reducers
, ou compondo manualmente todas as suas operações em uma única expressão, você fica mais fácil de usar abstrações, melhor modularidade e reutilização de funções de processamento.fonte
map
/reduce
para usar coleções intermediárias porque todos eles constroem uma cadeia iteradora. Onde estou errado aqui?map
efilter
crie coleções intermediárias quando aninhadas.Os transdutores são um meio de combinação para funções de redução.
Exemplo: funções de redução são funções que aceitam dois argumentos: um resultado até agora e uma entrada. Eles retornam um novo resultado (até agora). Por exemplo
+
: Com dois argumentos, você pode pensar no primeiro como o resultado até agora e no segundo como a entrada.Um transdutor agora pode pegar a função + e torná-la uma função duas vezes mais (duplica cada entrada antes de adicioná-la). É assim que esse transdutor seria (em termos mais básicos):
Para ilustração, substitua
rfn
por+
para ver como+
é transformado em duas vezes mais:assim
agora renderia 12.
As funções de redução retornadas pelos transdutores são independentes de como o resultado é acumulado porque elas se acumulam com a função de redução passada a eles, sem saber como. Aqui usamos em
conj
vez de+
.Conj
pega uma coleção e um valor e retorna uma nova coleção com esse valor anexado.renderia [2 4 6]
Eles também são independentes do tipo de fonte da entrada.
Vários transdutores podem ser encadeados como uma receita (encadeada) para transformar funções de redução.
Atualização: Como agora existe uma página oficial sobre o assunto, recomendo fortemente que a leia: http://clojure.org/transducers
fonte
double
etransduce
?Digamos que você queira usar uma série de funções para transformar um fluxo de dados. O shell Unix permite que você faça esse tipo de coisa com o operador pipe, por exemplo
(O comando acima conta o número de usuários com a letra r maiúscula ou minúscula em seu nome de usuário). Isso é implementado como um conjunto de processos, cada um dos quais lê a saída dos processos anteriores, portanto, há quatro fluxos intermediários. Você pode imaginar uma implementação diferente que compõe os cinco comandos em um único comando agregado, que lê sua entrada e grava sua saída exatamente uma vez. Se os fluxos intermediários fossem caros e a composição barata, isso poderia ser uma boa troca.
O mesmo tipo de coisa vale para Clojure. Existem várias maneiras de expressar um pipeline de transformações, mas dependendo de como você faz isso, você pode acabar com fluxos intermediários passando de uma função para a próxima. Se você tiver muitos dados, é mais rápido compor essas funções em uma única função. Os transdutores facilitam fazer isso. Uma inovação anterior do Clojure, os redutores, permitem que você faça isso também, mas com algumas restrições. Os transdutores removem algumas dessas restrições.
Então, para responder à sua pergunta, os transdutores não necessariamente tornarão seu código mais curto ou mais compreensível, mas seu código provavelmente não será mais longo ou menos compreensível também, e se você estiver trabalhando com muitos dados, os transdutores podem tornar seu código Mais rápido.
Esta é uma visão geral muito boa dos transdutores.
fonte
pmap
, o que parece não chamar atenção suficiente. Se você estivermap
executando o ping de uma função cara em uma sequência, tornar a operação paralela é tão fácil quanto adicionar "p". Não há necessidade de alterar mais nada em seu código, e ele está disponível agora - nem alfa, nem beta. (Se a função cria sequências intermediárias, então os transdutores podem ser mais rápidos, eu acho.)Rich Hickey deu uma palestra sobre 'Transdutores' na conferência Strange Loop 2014 (45 min).
Ele explica de forma simples o que são transdutores, com exemplos do mundo real - processamento de malas em um aeroporto. Ele separa claramente os diferentes aspectos e os contrasta com as abordagens atuais. Perto do fim, ele dá a justificativa de sua existência.
Vídeo: https://www.youtube.com/watch?v=6mTbuzafcII
fonte
Descobri que ler exemplos de transdutores-js me ajuda a entendê-los em termos concretos de como posso usá-los no código do dia-a-dia.
Por exemplo, considere este exemplo (retirado do README no link acima):
Por um lado, usar
xf
parece muito mais limpo do que a alternativa usual com Underscore.fonte
t.into([], t.comp(t.map(inc), t.filter(isEven)), [0,1,2,3,4])
Transdutores são (no meu entendimento!) Funções que assumem uma função de redução e retornam outra. Uma função redutora é aquela que
Por exemplo:
Neste caso, meu-transdutor assume uma função de filtragem de entrada que se aplica a 0, então se esse valor for par? no primeiro caso, o filtro passa esse valor para o contador e, em seguida, filtra o próximo valor. Em vez de primeiro filtrar e, em seguida, passar todos esses valores para a contagem.
É a mesma coisa no segundo exemplo, ele verifica um valor por vez e se esse valor for menor que 3, então ele permite contar somar 1.
fonte
Uma definição clara do transdutor está aqui:
Para entender isso, vamos considerar o seguinte exemplo simples:
Que tal, queremos saber quantas crianças há na aldeia? Podemos descobrir facilmente com o seguinte redutor:
Aqui está outra maneira de fazer isso:
Além disso, é muito poderoso quando se leva em conta subgrupos. Por exemplo, se quisermos saber quantas crianças há na Família Marrom, podemos executar:
Espero que esses exemplos sejam úteis. Você pode encontrar mais aqui
Espero que ajude.
Clemencio Morales Lucas.
fonte
Eu bloguei sobre isso com um exemplo de clojurescript que explica como as funções de sequência agora são extensíveis ao serem capazes de substituir a função de redução.
Este é o ponto dos transdutores conforme eu leio. Se você pensar sobre a operação
cons
ouconj
que está codificada em operações comomap
,filter
etc., a função de redução estava inacessível.Com transdutores, a função de redução é desacoplada e posso substituí-la como fiz com o array nativo de javascript,
push
graças aos transdutores.filter
e amigos têm uma nova operação 1 aridade que retornará uma função de transdução que você pode usar para fornecer sua própria função de redução.fonte
Aqui está meu jargão (principalmente) e resposta livre de códigos.
Pense nos dados de duas maneiras, um fluxo (valores que ocorrem ao longo do tempo, como eventos) ou uma estrutura (dados que existem em um ponto no tempo, como uma lista, um vetor, uma matriz, etc.).
Existem certas operações que você pode querer realizar sobre fluxos ou estruturas. Uma dessas operações é o mapeamento. Uma função de mapeamento pode incrementar cada item de dados (assumindo que seja um número) em 1 e você pode imaginar como isso poderia se aplicar a um fluxo ou estrutura.
Uma função de mapeamento é apenas uma de uma classe de funções que às vezes são chamadas de "funções de redução". Outra função de redução comum é o filtro que remove valores que correspondem a um predicado (por exemplo, remove todos os valores que são pares).
Os transdutores permitem "embrulhar" uma sequência de uma ou mais funções redutoras e produzir um "pacote" (que é uma função) que funciona em ambos os fluxos ou estruturas. Por exemplo, você pode "empacotar" uma sequência de funções de redução (por exemplo, filtrar números pares, em seguida, mapear os números resultantes para incrementá-los em 1) e, em seguida, usar esse "pacote" de transdutor em um fluxo ou estrutura de valores (ou ambos) .
Então, o que há de especial nisso? Normalmente, funções de redução não podem ser compostas de forma eficiente para funcionar em fluxos e estruturas.
Portanto, o benefício para você é que você pode alavancar seu conhecimento sobre essas funções e aplicá-las a mais casos de uso. O custo para você é que você precisa aprender algumas máquinas extras (ou seja, o transdutor) para fornecer essa potência extra.
fonte
Até onde eu entendo, eles são como blocos de construção , separados da implementação de entrada e saída. Você apenas define a operação.
Como a implementação da operação não está no código de entrada e nada é feito na saída, os transdutores são extremamente reutilizáveis. Eles me lembram de Flow em Akka Streams .
Eu também sou novo em transdutores, desculpe pela resposta possivelmente obscura.
fonte
Acho que este post dá a vocês uma visão mais panorâmica do transdutor.
https://medium.com/@roman01la/understanding-transducers-in-javascript-3500d3bd9624
fonte