Подтвердить что ты не робот

Почему редуктор Redux называется редуктором?

Во время обучения Redux я столкнулся с Reducers. В документации указано:

Редуктор - это чистая функция, которая принимает предыдущее состояние и действие и возвращает следующее состояние. (previousState, action) = > newState. Он называется редуктором, потому что это тип функции, которую вы передали бы Array.prototype.reduce(редуктор,? InitialValue).

MDN описывает метод reduce как:

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

Я все еще смущен тем, почему определение Redux редуктора не имеет смысла. Во-вторых, описание MDN тоже неверно. Метод reduce не всегда используется для уменьшения до одного значения. Его можно использовать вместо map и filter и на самом деле быстрее, когда используется вместо цепочки.

Неверное описание MDN?

Возвращаясь к определению Redux редуктора, он утверждает:

Он называется редуктором, потому что это тип функции, которую вы передали бы в Array.prototype.reduce(редуктор,? initialValue)

У меня создается впечатление, что редуктор Redux несет ответственность за изменение состояния. Пример редуктора:

const count = function(state, action) {
    if(action.type == 'INCREMENT') {
        return state + 1;
    } else if(action.type == 'DECREMENT') {
        return state - 1;
    } else {
        return state;
    }
}

... Я не вижу, как это функция, которая будет передана reduce. Как данные сводятся к одному значению? Если это функция, вы переходите к reduce, тогда state будет обратным вызовом, а action будет начальным значением.

Спасибо за любые ясные объяснения. Это сложно осмыслить.

4b9b3361

Ответ 1

Термин "уменьшить" на самом деле является функциональным термином, используемым в функциональном программировании. В таком языке, как Haskell, F # или даже JavaScript, мы определяем преобразование, которое принимает коллекцию (любого размера) в качестве входных данных и возвращает одно значение в качестве выходных.

Поэтому (не для того, чтобы быть педантичным, но я считаю, что это помогает мне) думать об этом визуально. У нас есть коллекция:

[][][][][][][][][][]

... который мы хотим свернуть в одно значение:

N

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

var accumulator = 0;
var myArray = [1,2,3,4,5];

myArray.reduce(function (each) {
    accumulator += 0;
});

return accumulator;

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

var myArray = [1,2,3,4,5];

return myArray.reduce(function (accumulator, each) {
    return accumulator + each;
}, 0);

В этом случае мы называем функцию "редуктором" из-за сигнатуры метода. У нас есть each (или current - любое имя подходит), представляющее объект в коллекции; и state (или previous), который передается на каждую итерацию функции, представляющий результаты преобразования, которое мы уже сделали с предыдущими элементами в коллекции.

Обратите внимание, что документация MDN, на которую вы ссылались, верна; функция reduce() всегда возвращает одно значение. Фактически, метод reduce на любом языке - это функция высшего порядка, которая принимает "редуктор" (функцию с сигнатурой метода, определенной выше) и возвращает одно значение. Теперь, да, вы можете делать с ним другие вещи, если ваша функция, которую вы вызываете, имеет побочные эффекты, но вы не должны. (По сути, не используйте .reduce() в качестве foreach.) Даже если у метода, который вы вызываете с помощью reduce, есть побочные эффекты, возвращаемое значение параметра Reduce само по себе будет одним значением, а не коллекцией.

Круто то, что этот шаблон должен применяться не только к массивам или конкретным коллекциям, как вы видели в React; этот шаблон также может быть применен к потокам, поскольку они являются чистыми функциями.

Надеюсь это поможет. Для чего бы это ни стоило, определение на сайте Redux могло бы быть улучшено (поскольку концепция редуктора не только из-за метода-прототипа Javascript Array). Вы должны представить пиар!

Изменение: Есть статья в Википедии на эту тему. Обратите внимание, что сокращение имеет разные имена, а в функциональных языках оно обычно называется Fold. https://en.wikipedia.org/wiki/Fold_(higher-order_function)#Folds_as_structural_transformations

Ответ 2

Причина, по которой редукторный редуктор называется reducer заключается в том, что вы могли "уменьшить" collection of actions и initial state (хранилища), в котором нужно выполнить эти действия, чтобы получить итоговое final state.

Как? Чтобы ответить на этот вопрос, позвольте мне снова определить редуктор:

Метод redu() применяет function (reducer) к accumulator и каждому значению массива (слева направо), чтобы уменьшить его до одного значения.

А что делает редуктор?

Редуктор - это чистая function которая принимает текущее состояние и действие и возвращает следующее состояние. Обратите внимание, что состояние accumulated поскольку каждое действие в коллекции применяется для изменения этого состояния.

Таким образом, с учетом collection of actions редуктор применяется к каждому значению набора (слева направо). В первый раз возвращает initial value. Теперь редуктор снова применяется к этому начальному состоянию и первое действие для возврата следующего состояния. И следующий элемент коллекции (действие) применяется каждый раз к current state чтобы получить next state пока оно не достигнет конца массива. И тогда вы получите the final state. Как это круто!

Ответ 3

Извините, но я не согласен с предыдущими ответами. Я бы не поддержал reducer именования. Я увлечен FP и неизменностью. Не вините меня, прочитайте вторую часть, но сначала я хочу сказать, почему я не согласен.

Правильно, что редукторы представляют собой последовательность преобразований, но сама последовательность - может быть частью другой последовательности. Вообразите это, как ссылки - часть цепи. Но сама цепь может быть частью более длинной цепи. Каждая ссылка является "переходом" глобального состояния. Чем, какая теория за этим стоит?

Разве это не "конечный автомат"? - близко, но нет. Это на самом деле Система Перехода.

Помеченная система переходов - это кортеж (S, Λ, →), где S - набор состояний, Λ - набор меток и → - набор помеченных переходов.

Итак, S - множество наших состояний

Λ - это наши так называемые "действия" (но метки в теории)

... а также

- редукторы "помеченные переходы"! Я бы назвал это так, если бы я был создателем этой библиотеки.

Понимание этой теории помогло мне реализовать мою библиотеку, где я могу иметь низкоуровневую систему переходов как часть высокоуровневой системы переходов (например, цепочка - все еще может быть частью более длинной цепочки) - и при этом иметь единое глобальное состояние Redux.

Ответ 4

Он называется редуктором, потому что это тип функции, которую вы передали бы в Array.prototype.reduce(редуктор,? initialValue)

Array.reduce

Это очень похоже на то, что вы передадите в Array.reduce в качестве обратного вызова (редуктора). Важная часть:

callback
  Function to execute on each value in the array, taking four arguments:
    previousValue
      The value previously returned in the last invocation of the callback, or initialValue, if supplied. (See below.)
    currentValue
      The current element being processed in the array.

Если состояние "предыдущее значение" и действие - "currentValue".

Ответ 5

У меня сложилось впечатление, что редуктор в Redux отвечает за изменение состояния. Пример редуктора:

const count = function(state, action) {
    if (action.type == 'INCREMENT') {
        return state + 1;
    } else if (action.type == 'DECREMENT') {
        return state - 1;
    } else {
        return state;
    }
}

... Я не понимаю, как эта функция будет передана для уменьшения. Как эти данные сводятся к одному значению? Если это функция, которую вы передадите, чтобы уменьшить, то состояние будет обратным вызовом, а действие будет начальным значением.

// count function from your question
const count = function (state, action) {
    if (action.type == 'INCREMENT') {
        return state + 1;
    } else if (action.type == 'DECREMENT') {
        return state - 1;
    } else {
        return state;
    }
}

// an array of actions
const actions =
  [ { type: 'INCREMENT' }
  , { type: 'INCREMENT' }
  , { type: 'INCREMENT' }
  , { type: 'INCREMENT' }
  , { type: 'DECREMENT' }
  ]

// initial state
const init = 0
  
console.log(actions.reduce(count, init))
// 3 (final state)
// (INCREMENT 4 times, DECREMENT 1 time)

Ответ 6

Эти ответы хороши, но я думаю, что это намного проще, чем вы думаете. Я признаю, что сначала у меня была похожая путаница. Метод Reduce в массивах Javascript использует аккумулятор и функцию обратного вызова.

const arr = [1, 2, 3]
const sum = arr.reduce((accumulator, element) => {
  accumulator += element;
  return accumulator;
}); // sum equals 6 now

Причина, по которой он называется редуктором в том, что он имеет примерно аналогичную структуру.

const sum = arr.reduce((accumulator, element) => {  // accumulator is the initial state
  accumulator += element; // we do something to modify the initial state
  return accumulator;  // we return that and it becomes the new state
}); 

Поэтому каждый раз, когда мы запускаем редуктор, мы что-то принимаем, модифицируем и возвращаем копию той же вещи. На каждой итерации мы указываем на одно и то же. Хорошо, да, мы должны сделать копию в redux, чтобы напрямую не изменять состояние, но символически каждый раз, когда мы его запускаем, это похоже на то, как Reduce начинается с начального состояния в примере выше 1. Затем мы добавляем 2 в исходное состояние и возвращаем 3. Теперь мы снова запускаем наш "редуктор" с начальным состоянием 3 и добавляем 3, и в итоге получаем 6.