Как работает функция reduce при возврате функции? Также хотелось бы узнать больше о compose и композиции

#javascript #function #functional-programming #reduce #composition

#javascript #функция #функциональное программирование #уменьшить #композиция

Вопрос:

Следующий код написан функциональным образом. Существует пользовательский объект, который собирается совершить покупку. Процесс выглядит следующим образом: товар добавляется в корзину, добавляется налог, товар покупается и помещается в массив истории покупок пользователя. Затем, наконец, исходная корзина очищается. Все это делается с помощью функции reduce, в которую передана функция compose.

Что я изо всех сил пытаюсь понять, так это то, как работает функция reduce в этом случае. Насколько я понимаю, здесь функция compose передается как функция обратного вызова для reduce. внутри функции compose f представляет накопитель, а g представляет элемент в массиве.

  const user = {
  name: 'Kim',
  active: true,
  cart: [],
  purchases: []
}

const compose = (f,g) => (...args) =>  {
  console.log(1, f);
  console.log(2, g);
  return f(g(...args));
  }

// The below code is commented out so that I can visualize it in a more simple manner
//purchaseItem (
 // emptyCart,
 // buyItems,
  //applyTaxToItems,
 // addItemToCart
//)(user, {name: 'laptop', price: 244});

function purchaseItem(...fns) {
  console.log(fns);
  return fns.reduce(compose);
}

// Here is each piece broken down to be passed into reduce and the arguments the compose function takes
console.log([emptyCart , buyItems, applyTaxToItems, addItemToCart].reduce(compose)(user, {name: 'laptop', price: 244}));

function addItemToCart(user, item) {
  const updateCart = user.cart.concat(item);
  return Object.assign({}, user, { cart: updateCart });
}

function applyTaxToItems(user, item) {
  return user
}

function buyItems(user, item) {
  return user
}

function emptyCart(user, item) {
  return user
} 
  

Результат выглядит следующим образом:

 1 [Function]
2 [Function: addItemToCart]
1 [Function]
2 [Function: applyTaxToItems]
1 [Function: emptyCart]
2 [Function: buyItems]
{ name: 'Kim',
  active: true,
  cart: [ { name: 'laptop', price: 244 } ],
  purchases: [] }  
  

Я попытался отобразить поток элементов f и g. Я понимаю, что f будет содержать любое значение, возвращаемое функцией compose, но почему начальное значение является анонимной функцией. Кроме того, почему элемент item начинается с последнего элемента в массиве и работает в обратном направлении? Я также не понимаю, почему функция emptyCart становится значением f в последнем цикле уменьшения. Если бы кто-нибудь мог мне это объяснить, я был бы чрезвычайно благодарен. Спасибо.

Комментарии:

1. Учитывая, что все функции в вашем массиве принимают два аргумента, нет особого смысла их составлять.

2. Ваше сокращение не возвращает функцию. Он применяет список функций одну за другой. Для всех последующих после первого второй аргумент игнорируется.

3. Почему второй аргумент игнорируется для всех последующих, кроме первого?

Ответ №1:

Ваши журналы неправильно отображают поток выполнения.

Вы регистрируетесь f первым и g вторым, но для f(g(x)) , g вычисляется первым и f вторым. f(g(x) можно прочитать «f после g из x» или «f из g из x».

Таким же образом, когда вы сокращаете массив функций с помощью редуктора типа f(g(...x)) , они будут оцениваться в обратном порядке, поскольку ваша результирующая функция ведет себя как f(g(h(x))) .

Смотрите код ниже для более подробного объяснения. compose2 это то же самое, что и ваша compose функция, но с более интенсивным ведением журнала.

Если вы запустите приведенный ниже код, вы можете получить лучшее представление о том, что происходит. Чтобы оценить функцию, созданную с помощью сокращения / композиции, мы оцениваем несколько функций формы f(g(...args)) .

Обратите внимание, как результаты g(...args) всего распространяются вверх, прежде чем конечный результат распространяется обратно вниз.

 const compose2 = (f, g, i) => {
  const name = `${f.name} after ${g.name}`;
  const h = {[name]: (...args) =>  {
    console.log(i, 'f:', f.name);
    console.log(i, 'g:', g.name);
    console.log(i, 'args to g:', ...args);
    console.log(i, 'g(...args):', g(...args));
    console.log(' ');
    const res = f(g(...args));
    console.log(i, 'result:', res);
    return res;
  }}[name];
  return h;
}

const f_xe = x => x   'e', 
      f_xd = x => x   'd', 
      f_xc = x => x   'c',
      f_xy = (x, y) => x   y;

console.log([f_xe, f_xd, f_xc, f_xy].reduce(compose2)('a','b'));  

Комментарии:

1. Не могли бы вы более подробно объяснить вывод? Мне все еще трудно разобраться в этом. Я был бы очень признателен.