Является ли мое понимание и JS код о преобразователе правильно - PullRequest
2 голосов
/ 08 июня 2019

веб-статей, которые я читал о преобразователе

Js

  • Преобразователи: эффективные конвейеры обработки данных в JavaScript @ Eric Elliott -Medium
  • Понимание преобразователей в JavaScript@ Роман Лютиков-Средний

трудно понять наполовину ...

  • Что такое преобразователь?
  • Более простые преобразователи для JavaScript
  • Как сделать ваши преобразования данных более эффективными, используя преобразователи

Clojure

  • ПРИБОРЫ ПРЕОБРАЗОВАТЕЛЕЙ от Rich Hickey-cognitect
  • Transducers-Clojure.org

Я прочитал официальный учебник Clojure примерно на 2 страницах и понял основной синтаксис.Я сослался на ссылку на встроенную функцию, чтобы понять пример кода преобразователя.

мое понимание вышеупомянутой двух статей, вероятно, составляет 75% ...

мой вопрос

Я хочучтобы знать, является ли следующее понимание / JS код является правильным или неправильным.Пожалуйста, помогите мне. <(_ _)>

О преобразователе

  1. Возвращаемое значение compose() является преобразователем.
  2. Преобразователь выполняется переданным вtransduce() функция в качестве аргумента и, кроме того, (2) преобразователь выполняется путем передачи массива непосредственно в transducer().
  3. В процессе (2) промежуточное значение не генерируется иэффективный процесс, как показано ниже ссылка выполняется.

мой код

"use strict";

const map = fn => arr => arr.map(fn),
filter = fn => arr => arr.filter(fn),
addReducer = arr => arr.reduce((acc, num) => acc + num, 0),
add1 = n => n + 1,
even = n => n % 2 === 0,

compose = (...fns) => initVal => fns.reduce((acc, fn) => fn(acc), initVal),
transduce = (xform, reducer, arr ) => reducer( xform(arr) );



const arr = [1,2,3],
transducer = compose(  /* called transducer or xform */
   map( add1 ), // 2,3,4
   filter( even ), // 2,4
);

console.log( transducer(arr) ) // 2,4
console.log( transduce(transducer, addReducer, arr) ) // 6

Ответы [ 2 ]

1 голос
/ 09 июня 2019

Ваш код не имеет ничего общего с преобразователями.Ваше определение filter и m̀ap показывает, что оно использует обычные JS filter и map

const map = fn => arr => arr.map (fn),
const filter = fn => arr => arr.filter (fn),

const combo = compose(map(add1), filter(even));
combo(arr); ==> [2, 4]

. В результате исходный массив передается в map с add1который будет производить массив [2, 3, 4], а затем он будет передан filter с even и созданным новым массивом [2, 4].

То же самое в преобразователях:

const arr = [1, 2, 3];

const add1 = n => n + 1;
const even = n => n% 2 === 0;

const compose = (...fns) => {
  const [firstFunc, ...restFuncs] = fns.reverse();
  return (...args) => restFuncs.reduce((acc, fn) => fn(acc), firstFunc(...args));
};

const mapping = 
  fn => join => (acc, e) => join(acc, fn(e));

const filtering = 
  isIncluded => join => (acc, e) => isIncluded(e) ? join(acc, e) : acc;

const transducer = compose(mapping(add1), filtering(even));
const arrayJoin = (acc, e) => ([...acc, e]);
const result = arr.reduce(transducer(arrayJoin), []);
console.log(result);

Таким образом, разница в том, что когда вы передаете join преобразователю, происходит следующее:

mapping(add1)(filtering(even)(arrayAdd))

filtering - единственный шагэто добавляет к некоторой коллекции.Когда mapping звонит join, звонит filtering напрямую.Вот почему подписи (acc, e) одинаковы для рабочей части и функции join.Когда код запускается, добавление и фильтрация выполняются одновременно, в результате получается только один сгенерированный массив без промежуточных значений.

1 голос
/ 08 июня 2019

Преобразователи используют тот факт, что композиция функций абстрагируется от арности, т.е. может возвращать функцию вместо «нормального значения»:

const comp = f => g => x => f(g(x));

const add = x => y => x + y;

const sqr = x => x * x;

const add9 = comp(add) (sqr) (3); // returns a lambda

console.log(
  add9(6)); // 15

Теперь сам преобразователь довольно скучен:

reduce => acc => x => /* body is specific to the transducer at hand */

Это просто замыкание, которое ожидает редуктор (то есть двоичная функция, которая объединяет свои двааргументы) и затем могут быть переданы непосредственно в вашу любимую сокращающую функцию.

Давайте посмотрим на преобразователь карты:

const mapper = f => (reduce => acc => x =>
  reduce(acc) (f(x)));

Избыточные скобки просто иллюстрируют замыкание преобразователя.В этом случае он закрывается над f, нашей функцией преобразования.Далее мы собираемся применить его:

// map transducer

const mapper = f => reduce => acc => x =>
  reduce(acc) (f(x));

// my favorite fold (reducing function)

const arrFold = alg => zero => xs => {
  let acc = zero;

  for (let i = 0; i < xs.length; i++)
    acc = alg(acc) (xs[i], i);

  return acc;
};

// reducer

const add = x => y => x + y;

// transformer

const sqr = x => x * x;

// MAIN

const main = arrFold(mapper(sqr) (add)) (0);

console.log(
  main([1,2,3])); // 14

Ну, не так впечатляет, верно?Реальная мощность преобразователей обусловлена ​​их сочетанием с функциональным составом:

// map transducer

const mapper = f => reduce => acc => x =>
  reduce(acc) (f(x));

// filter transducer

const filterer = p => reduce => acc => x =>
  p(x) ? reduce(acc) (x) : acc;
  
// my favorite fold (reducing function)

const arrFold = alg => zero => xs => {
  let acc = zero;

  for (let i = 0; i < xs.length; i++)
    acc = alg(acc) (xs[i], i);

  return acc;
};

// helpers

const add = x => y => x + y; // reducer
const sqr = x => x * x; // transformer
const isOdd = x => (x & 1) === 1; // predicate
const comp = f => g => x => f(g(x));

// MAIN

const main = arrFold(comp(filterer(isOdd)) (mapper(sqr)) (add)) (0);

console.log(
  main([1,2,3])); // 10

Несмотря на то, что у нас есть два преобразователя, через Array есть только один обход.Это свойство называется циклическим слиянием.Поскольку композиция преобразователя возвращает другую функцию, порядок оценки меняется на противоположный, то есть идет слева направо, тогда как композиция функции обычно идет справа налево.

Повторное использование является еще одним преимуществом.Вы должны определить преобразователи только один раз, и можете использовать их один раз и использовать все складываемые типы данных.

Также следует отметить, что transduce - это просто вспомогательная функция и не важна для понимания концепции.

Это почти то же самое, что сказать о преобразователях.

...