Правильно ли я понимаю и код js о преобразователе?

веб-статьи, которые я читал о датчике

Js

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

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

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

Кложур

  • ПРЕОБРАЗОВАТЕЛИ ПРИБЛИЖАЮТСЯ by Rich Hickey-cognitect
  • Преобразователи-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.info( transducer(arr) ) // 2,4
console.info( transduce(transducer, addReducer, arr) ) // 6
compose неправильно. При прохождении compose(a, b) превращается в b(a(initVal)), а применять нужно в обратном порядке a(b(initVal))
Sylwester 08.06.2019 23:29

Спасибо, что ответили. Я понял. compose() является правоассоциативным, а pipe() — левоассоциативным, верно?

AzumaO 09.06.2019 08:20

Да, вы реализовали pipe.

Sylwester 09.06.2019 15:30

Связано: stackoverflow.com/questions/52274362/…

Jared Smith 28.08.2019 14:14
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
4
4
766
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

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

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 — это просто функция удобства, и она не важна для понимания концепции».

AzumaO 09.06.2019 08:51

Ваш код не имеет ничего общего с датчиками. Ваше определение 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 это важная, ключевая часть... (продолжайте понимать сейчас...) очень-очень большая!

AzumaO 09.06.2019 09:06

Другие вопросы по теме