Каким образом Javascript `уменьшение`, выполняемый на массиве функций, достигает композиции функций? - PullRequest
0 голосов
/ 24 марта 2019

Я сталкивался с этим шаблоном в функции redux compose. Я до сих пор не понимаю, как в примере ниже функции оцениваются, начиная с последнего, а не с первого:

function f2(a) {
  return a + a;
}
function f3(a) {
  return a + a + a;
}
function f4(a) {
  return a + a + a + a;
}
function f5(a) {
  return a + a + a + a + a;
}

function compose(...funcs) {
  return funcs.reduce(function x(a, b) {
    return function y(...args) {
      const temp = a(b(...args));
      return temp;
    };
  });
}

const composedFunction = compose(f2, f3, f4, f5);
const result = composedFunction(2);

В первой итерации reduce аккумулятор равен f2, поэтому мы получим f2(f3(2))=12. На следующей итерации мы назовем f4(12)=48. На последней итерации мы будем называть f5(48)=240. Таким образом, порядок оценки составляет f5(f4(f2(f3(2)))). Но, используя console.log, я вижу, что порядок оценки равен f2(f3(f4(f5(2)))), что также равно 240 по совпадению.

Насколько я понимаю, функция y вызывается для всех элементов массива, так почему только последняя функция получает 2 в качестве параметра?

Ответы [ 3 ]

2 голосов
/ 24 марта 2019

Давайте рассмотрим код на очень простом примере:

 compose(f2, f3, f4)

Поскольку не было передано начальное значение для уменьшения, оно начнется с первого (f2) и второго (f3) значениямассив и вызов обратного вызова с этим, x вызывается с a, являющимся f2 и b, являющимся f3.Теперь x ничего не делает, просто возвращает функцию y, которая может получить доступ к a и b через замыкание.

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

Если мы попытаемся визуализировать таким образом закрытую функцию, это будет:

 y { // closure of y
  a -> y { // a references another closure of y
    a -> f3,
    b -> f2
  },
  b -> f4
}

Теперь вы вызываете закрытое y и передаете в него 2, что вызовет b (f4) и передают результат в вызов a (закрытое y).

 a         (  b(...args))   
 y { ... } (  f4(2) )

Теперь, когда замыкание y будет делать то же самое:

 a (  b ( ...args))
 f2( f3( f4( 2 ) ) )

Подсказка: Иногда очень трудно отслеживать закрытые значения, поэтомуконсоль предоставляет вам отличные утилиты для их отслеживания: откройте ваш код на вкладке «отладчик» консоли, щелкните по номерам строк, где вызовы функций предназначены для присоединения точек останова, затем снова запустите код, выполнение будет выполняться всякий раз, когдаТочка останова достигнута, и вы можете видеть значения всех переменных (включая закрытые).

1 голос
/ 24 марта 2019

Функция compose может быть переписана следующим образом:

function compose(...funcs) {
  return funcs.reduce(function (a, b) {
    return function (arg) {
      const temp = a(b(arg));
      return temp;
    };
  });
}

После первой итерации возвращается функция, которая передается в качестве следующего аккумулятора:

function (arg) {       // R1
  return f2(f3(arg));

}

После второй итерации возвращаемая функция, которая передается в качестве следующего аккумулятора:

function (arg) {       // R2
  return R1(f4(arg));

}

И, наконец, возвращаемая функция, присвоенная mixedFunction :

function (arg) {       // composedFunction
  return R2(f5(arg));

}

Итак, работаем composedFunction(2) и возвращаемся по цепочке:

f5(2) returns 10

R2(10) returns R1(f4(10))
which is       R1(40)

R1(40) returns f2(f3(40))
which is   f2(120)
which is   240

Надеюсь, этого достаточно.

Он может быть записан как один вызов как:

function composedFunction(arg) {
  return f2(f3(f4(f5(arg))));
}
1 голос
/ 24 марта 2019

Редакция не вызывает функции f2, f3, f3, f5, но создает функцию из них.Это значение аккумулятора в каждой итерации.Обратите внимание, что это значение function, а не результат выполнения функции.

1: a = f2; b = f3; возвращаемое значение (НЕ ТЕМП, а функция y) = f2 (f3 (...args))

2: a (предыдущее возвращаемое значение) = f2 (f3 (... args)); b = f4; возвращаемое значение = f2 (f3 (f4 (... args)))

и тд ....

...