Преобразователи - это рецепты того, что делать с последовательностью данных, не зная, какова основная последовательность (как это сделать). Это может быть любой seq, асинхронный канал или, возможно, наблюдаемый.
Они компонуются и полиморфны.
Преимущество в том, что вам не нужно реализовывать все стандартные комбинаторы каждый раз, когда добавляется новый источник данных. Опять и опять. В результате вы, как пользователь, можете повторно использовать эти рецепты в разных источниках данных.
Обновление объявления
В предыдущей версии Clojure 1.7 у вас было три способа писать запросы потока данных:
- вложенные вызовы
(reduce + (filter odd? (map #(+ 2 %) (range 0 10))))
- функциональный состав
(def xform
(comp
(partial filter odd?)
(partial map #(+ 2 %))))
(reduce + (xform (range 0 10)))
- макрос потоковой передачи
(defn xform [xs]
(->> xs
(map #(+ 2 %))
(filter odd?)))
(reduce + (xform (range 0 10)))
С преобразователями вы запишете это так:
(def xform
(comp
(map #(+ 2 %))
(filter odd?)))
(transduce xform + (range 0 10))
Все они делают то же самое. Разница в том, что вы никогда не вызываете преобразователи напрямую, вы передаете их другой функции. Преобразователи знают, что делать, функция, которая получает преобразователь, знает как. Порядок комбинаторов такой же, как если бы вы написали его с помощью макроса многопоточности (естественный порядок). Теперь вы можете повторно использовать xform
канал:
(chan 1 xform)
Преобразователи повышают эффективность и позволяют писать эффективный код более модульным способом.
Это приличный пробег .
По сравнению с сочиняли вызовы к старому
map
,filter
, иreduce
т.д. Вы получаете более высокую производительность , так как вам не нужно , чтобы построить промежуточные коллекции каждого шага, и несколько раз ходить эти коллекции.По сравнению с
reducers
объединением всех ваших операций в одно выражение или ручным объединением всех ваших операций в одно выражение вам становится проще использовать абстракции, улучшается модульность и многократное использование функций обработки.источник
map
/reduce
использования промежуточных коллекций, потому что все они создают цепочку итераторов. Где я здесь не прав?map
иfilter
создание промежуточных коллекций при вложении.Преобразователи представляют собой средства комбинации для уменьшения функций.
Пример. Редукционные функции - это функции, которые принимают два аргумента: текущий результат и вход. Возвращают новый результат (пока). Например
+
: с двумя аргументами вы можете думать о первом как о результате, а о втором как о вводе.Преобразователь теперь может взять функцию + и сделать ее двойной (удваивает каждый вход перед добавлением). Вот как этот преобразователь будет выглядеть (в самых общих чертах):
Для иллюстрации замените
rfn
на,+
чтобы увидеть, как+
он превращается в дважды плюс:Так
теперь даст 12.
Функции сокращения, возвращаемые преобразователями, не зависят от того, как накапливается результат, потому что они накапливаются вместе с переданной им функцией сокращения, не зная, как. Здесь мы используем
conj
вместо+
.Conj
принимает коллекцию и значение и возвращает новую коллекцию с добавленным этим значением.даст [2 4 6]
Они также не зависят от источника входного сигнала.
Несколько преобразователей могут быть объединены в цепочку (цепочку) для преобразования восстанавливающих функций.
Обновление: поскольку теперь есть официальная страница об этом, я настоятельно рекомендую прочитать ее: http://clojure.org/transducers
источник
double
иtransduce
?Допустим, вы хотите использовать серию функций для преобразования потока данных. Оболочка Unix позволяет делать такие вещи с помощью оператора pipe, например
(Приведенная выше команда подсчитывает количество пользователей, у которых в имени пользователя есть буква r в верхнем или нижнем регистре). Это реализовано в виде набора процессов, каждый из которых считывает выходные данные предыдущего процесса, поэтому существует четыре промежуточных потока. Вы можете представить себе другую реализацию, которая объединяет пять команд в одну агрегированную команду, которая будет читать из своего ввода и записывать свой вывод ровно один раз. Если бы промежуточные потоки были дорогими, а композиция - дешевой, это могло бы быть хорошим компромиссом.
То же самое и с Clojure. Существует несколько способов выразить конвейер преобразований, но в зависимости от того, как вы это делаете, вы можете получить промежуточные потоки, переходящие от одной функции к другой. Если у вас много данных, быстрее объединить эти функции в одну функцию. Преобразователи позволяют легко это сделать. Более ранняя инновация Clojure, редукторы, тоже позволяет делать это, но с некоторыми ограничениями. Преобразователи снимают некоторые из этих ограничений.
Итак, чтобы ответить на ваш вопрос, преобразователи не обязательно сделают ваш код короче или более понятным, но ваш код, вероятно, также не будет длиннее или менее понятным, и если вы работаете с большим количеством данных, преобразователи могут сделать ваш код Быстрее.
Это довольно хороший обзор преобразователей.
источник
pmap
, что, кажется, не привлекает достаточно внимания. Если выmap
проверяете дорогостоящую функцию над последовательностью, сделать операцию параллельной так же просто, как добавить «p». Больше ничего менять в коде не нужно, и он доступен сейчас - ни альфа, ни бета. (Если функция создает промежуточные последовательности, тогда преобразователи могут быть быстрее, я полагаю.)Рич Хики выступил с докладом «Transducers» на конференции Strange Loop 2014 (45 мин).
Он просто объясняет, что такое преобразователи, на реальных примерах - обработка сумок в аэропорту. Он четко разделяет различные аспекты и противопоставляет их нынешним подходам. Ближе к концу он дает обоснование их существования.
Видео: https://www.youtube.com/watch?v=6mTbuzafcII
источник
Я обнаружил, что чтение примеров из transducers-js помогает мне понять их в конкретных терминах того, как я могу использовать их в повседневном коде.
Например, рассмотрим этот пример (взятый из README по ссылке выше):
Во-первых, использование
xf
выглядит намного чище, чем обычная альтернатива с Underscore.источник
t.into([], t.comp(t.map(inc), t.filter(isEven)), [0,1,2,3,4])
Преобразователи - это (насколько я понимаю!) Функции, которые принимают одну уменьшающую функцию и возвращают другую. Редукционная функция - это функция, которая
Например:
В этом случае my-transducer принимает функцию входной фильтрации, которая применяется к 0, тогда, если это значение четное? в первом случае фильтр передает это значение счетчику, а затем фильтрует следующее значение. Вместо того, чтобы сначала фильтровать, а затем передавать все эти значения для подсчета.
То же самое и во втором примере: он проверяет одно значение за раз, и если это значение меньше 3, он позволяет count добавить 1.
источник
Четкое определение преобразователя здесь:
Чтобы понять это, давайте рассмотрим следующий простой пример:
Что по этому поводу мы хотим знать, сколько детей в селе? Мы можем легко узнать это с помощью следующего редуктора:
Вот еще один способ сделать это:
Кроме того, он действительно эффективен при учете подгрупп. Например, если мы хотим узнать, сколько детей в семье Браун, мы можем выполнить:
Надеюсь, эти примеры вам пригодятся. Вы можете найти больше здесь
Надеюсь, поможет.
Клеменсио Моралес Лукас.
источник
Я писал об этом в блоге с помощью примера clojurescript, который объясняет, как функции последовательности теперь расширяются за счет возможности замены функции сокращения.
В этом суть преобразователей, как я это читал. Если вы думаете об операции
cons
илиconj
, жестко закодированной в таких операциях, какmap
иfilter
т. Д., Функция сокращения была недоступна.С преобразователями функция уменьшения не связана, и я могу заменить ее, как я это сделал с собственным массивом javascript,
push
благодаря преобразователям.filter
и у друзей есть новая операция 1 arity, которая вернет функцию преобразования, которую вы можете использовать для предоставления вашей собственной функции сокращения.источник
Вот мой (в основном) жаргон и ответ без кода.
Подумайте о данных двумя способами: поток (значения, которые возникают во времени, например события) или структура (данные, существующие в определенный момент времени, например список, вектор, массив и т. Д.).
Есть определенные операции, которые вы можете выполнять над потоками или структурами. Одна из таких операций - отображение. Функция сопоставления может увеличивать каждый элемент данных (при условии, что это число) на 1, и вы, надеюсь, можете представить, как это можно применить к потоку или структуре.
Функция отображения - это всего лишь одна из класса функций, которые иногда называют «сокращающими функциями». Другой распространенной функцией сокращения является фильтр, который удаляет значения, соответствующие предикату (например, удаляет все четные значения).
Преобразователи позволяют «обернуть» последовательность из одной или нескольких сокращающих функций и создать «пакет» (который сам по себе является функцией), который работает с обоими потоками или структурами. Например, вы можете «упаковать» последовательность сокращающих функций (например, отфильтровать четные числа, затем сопоставить полученные числа, чтобы увеличить их на 1), а затем использовать этот «пакет» преобразователя либо в потоке, либо в структуре значений (или в обоих) .
Так что же в этом особенного? Как правило, редуцирующие функции не могут быть эффективно скомпонованы для работы как с потоками, так и со структурами.
Таким образом, вы можете воспользоваться своими знаниями об этих функциях и применить их к большему количеству вариантов использования. Цена для вас состоит в том, что вы должны изучить некоторые дополнительные механизмы (например, преобразователь), чтобы дать вам эту дополнительную мощность.
источник
Насколько я понимаю, они похожи на строительные блоки , отделенные от реализации ввода и вывода. Вы просто определяете операцию.
Поскольку реализация операции отсутствует во входном коде и ничего не делается с выходом, преобразователи чрезвычайно многоразовые. Они напоминают мне Flow в Akka Streams .
Я также новичок в датчиках, извините за возможно неясный ответ.
источник
Я считаю, что этот пост дает вам более полное представление о датчике с высоты птичьего полета.
https://medium.com/@roman01la/understanding-transducers-in-javascript-3500d3bd9624
источник