трудно понять с половины...
Я прочитал официальное руководство по Clojure на 2 страницах и понял основной синтаксис. Я обратился к справочнику по встроенным функциям, чтобы понять пример кода преобразователя.
мое понимание двух предыдущих статей, вероятно, составляет 75% ...
Я хочу знать, является ли следующий код понимания/js правильным или неправильным. Пожалуйста, помогите мне.<(_ _)>
compose(), является преобразователем.transduce() в качестве аргумента, и, кроме того, (2) преобразователь выполняется путем передачи массива непосредственно в transducer().В процессе (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.info( transducer(arr) ) // 2,4
console.info( transduce(transducer, addReducer, arr) ) // 6
Спасибо, что ответили. Я понял. compose() является правоассоциативным, а pipe() — левоассоциативным, верно?
Да, вы реализовали pipe.
Связано: stackoverflow.com/questions/52274362/…



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Преобразователи используют тот факт, что композиция функций абстрагируется от арности, т.е. может возвращать функцию вместо «нормального значения»:
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.info(
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.info(
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.info(
main([1,2,3])); // 10Несмотря на то, что у нас задействовано два преобразователя, есть только один проход через Array. Это свойство называется слиянием петель. Поскольку состав преобразователя возвращает другую функцию, порядок оценки обратный, то есть идет слева направо, тогда как состав функций обычно идет справа налево.
Возможность повторного использования является еще одним преимуществом. Вы должны определить преобразователи только один раз и можете использовать их один раз и навсегда со всеми складируемыми типами данных.
Также следует отметить, что transduce — это просто функция удобства, и она не важна для понимания концепции.
Это в значительной степени то, что можно сказать о преобразователях.
Спасибо, что ответили. Я читаю и пытаюсь понять ваш ответ. очень большое спасибо. В частности, два предложения вашего ответа заставили меня осознать: (1) «Преобразователи используют тот факт, что композиция функций абстрагируется от арности, т.е. может возвращать функцию вместо «нормального значения». (2) «Примечательно также, что Transduce — это просто функция удобства, и она не важна для понимания концепции».
Ваш код не имеет ничего общего с датчиками. Ваше определение 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.info(result);Таким образом, разница в том, что когда вы передаете join преобразователю, происходит следующее:
mapping(add1)(filtering(even)(arrayAdd))
filtering — единственный шаг, который пополняет коллекцию. Когда mapping звонит join, он звонит filtering напрямую. Вот почему подписи (acc, e) одинаковы для рабочей части и функции join. Когда код запускается, добавление и фильтрация выполняются одновременно, в результате получается только один сгенерированный массив и нет промежуточных значений.
Спасибо за ответ. О, я понял первую половину твоего ответа! А теперь попробуй понять вторую половину. join это важная, ключевая часть... (продолжайте понимать сейчас...) очень-очень большая!
composeнеправильно. При прохожденииcompose(a, b)превращается вb(a(initVal)), а применять нужно в обратном порядкеa(b(initVal))