为什么Redux减速器称为减速器?

时间:2016-11-14 23:20:21

标签: javascript reactjs redux functional-programming

在学习Redux的同时,我遇到了Reducers。文档说明:

  

reducer是一个纯函数,它接受前一个状态和一个动作,并返回下一个状态。 (previousState,action)=> newState。它被称为reducer,因为它是你传递给Array.prototype.reduce(reducer,?initialValue)的函数类型。

MDN将reduce方法描述为:

  

reduce()方法对累加器和数组的每个值(从左到右)应用一个函数,将其减少为单个值。

我仍然对Redux定义减速器的原因感到困惑,因为它没有任何意义。其次,MDN描述似乎也不正确。 reduce方法并不总是用于减少单个值。它可用于代替mapfilter,并且在用于代替链接时实际上更快。

MDN描述不正确吗?

回到Redux定义的reducer,它声明:

  

它被称为reducer,因为它是你传递给Array.prototype.reduce(reducer,?initialValue)的函数类型

我的印象是Redux中的reducer负责修改状态。减速器示例:

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将是初始值。

感谢您提供任何明确的解释。这很难概念化。

9 个答案:

答案 0 :(得分:23)

术语"减少"实际上是函数式编程中使用的函数术语。在像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);

在这种情况下,我们将函数称为" reducer"因为它的方法签名。我们有each(或current - 任何名称都没问题),表示集合中的对象;和state(或previous),传递给函数的每次迭代,表示我们已经对集合中的前一个元素进行的转换的结果。

请注意,您引用的MDN文档是正确的; reduce()函数始终返回单个值。事实上,任何语言中的reduce方法都是higher-order function,需要" reducer" (具有上面定义的方法签名的函数)并返回单个值。现在,是的,你可以做其他事情,如果你调用的函数有副作用,但你不应该。 (基本上,不要使用.reduce()作为foreach。)即使您使用reduce调用的方法有副作用,reduce本身的返回值也将是单个值,而不是集合。

关于这一点很酷,这个模式并不仅仅适用于数组或具体集合,正如你在React中看到的那样;这种模式也可以应用于流,因为它们是纯函数。

希望这会有所帮助。对于它的价值,可以改进Redux站点上的定义(因为减速器的概念不仅仅是因为Javascript的数组原型方法)。你应该提交公关!

编辑:有一篇关于这个主题的维基百科文章。请注意,reduce具有不同的名称,在函数式语言中,它通常称为Fold。 https://en.wikipedia.org/wiki/Fold_(higher-order_function)#Folds_as_structural_transformations

答案 1 :(得分:10)

  

redux reducer被称为reducer的原因是因为您可以"减少"一个collection of actions和一个initial state(商店),可以在其上执行这些操作以获得结果final state

如何?要回答这个问题,让我再次定义一个减速器:

  

reduce()方法对function (reducer)accumulator应用function   数组的值(从左到右)将其减少为单个   值。

还原剂有什么作用?

  

reducer是一个纯accumulated,它采用当前状态和   动作,并返回下一个状态。请注意,状态为collection of actions,因为应用集合上的每个操作都会更改此状态。

因此,给定initial value,reducer将应用于集合的每个值(从左到右)。第一次,它返回current state。现在,reducer再次应用于此初始状态,并且第一个操作将返回下一个状态。并且每次在next state上应用下一个集合项(操作)以获取the final state,直到它到达数组的末尾。然后,你得到{{1}}。这有多酷!

答案 2 :(得分:3)

抱歉,但我不同意以前的答案。我不会支持命名reducer。我对FP和不变性充满热情。不要责怪我,请阅读第二部分,但我想首先说明,为什么我不同意。

正确的是,reducer是转换序列,但序列本身 - 可能是另一个序列的一部分。想象一下,就像链接一样 - 链条的一部分。但链条本身可能是更长链的一部分。每个环节都是全球国家的“过渡”。比,它背后的理论是什么?

它实际上不是“有限状态机”吗? - 关闭,但不是。它实际上是Transition system

  

标记过渡系统是元组(S,Λ,→),其中S是一组状态,Λ是一组标签,→是一组标记过渡

所以,S - 是我们的状态集

Λ - 是我们所谓的“行动”(理论上是标签

...和

- redurs “标记过渡”!如果我是这个图书馆的创建者,我会这么说。

理解这个理论帮助我实现了我的库,我可以将低级转换系统作为高级转换系统的一部分(如链 - 仍然可能是更长链的一部分 - 并且仍然具有单一的全局Redux状态。

答案 3 :(得分:2)

  

它被称为reducer,因为它是你传递给Array.prototype.reduce(reducer,?initialValue)的函数类型

Array.reduce

这与将作为回调(reducer)传递给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.

其中state是" previousValue"而行动是" currentValue"。

答案 4 :(得分:1)

  

我的印象是Redux中的reducer负责修改状态。减速器示例:

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)

答案 5 :(得分:0)

这些答案很好,但我认为这比您想象的要简单得多。我承认一开始我也有类似的困惑。 Javascript数组上的reduce方法需要一个累加器和一个回调函数。

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

在redux中被称为reducer的原因是因为它的结构大致相似。

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
}); 

因此,每当我们运行一个reducer时,我们都会接受一些东西,对其进行修改,然后返回相同内容的副本。在每次迭代中,我们都指向同一件事。好的,是的,我们必须在redux中创建一个副本,以便不直接修改状态,但是象征性地,每次运行它时,就像在上面的示例1中,reduce以初始状态开始的方式一样,然后添加2回到初始状态,然后返回3。现在,我们以3的初始状态再次运行“ reducer”,然后加3,最后得到6。

答案 6 :(得分:0)

这是一个令人困惑的名称,因为实际上它与 Array.prototype.reduce 无关。

在 Redux 源代码 here 中,您的减速器是这样调用的:

currentState = currentReducer(currentState, action)

使用这个词很诱人,因为您可以通过调用 arrayOfPreviousActions.reduce(reducer) 随时找到您的状态。

['INCREMENT', 'INCREMENT'].reduce((currentState, action) => currentState++, 0)

但实际上并不是这样称呼的。


真正的问题是,用什么名字更合适?

如果我们不考虑输出/目的,它就是在处理一个动作/事件。一个 actionHandleractionProcessor

旁白:event 也可能是比 action 更好的术语,因为 Redux 也经常用于响应 API 响应,这导致操作类型设置为 GET_USERS_SUCCESS.. .这显然是一个事件。不过,这只是从 Flux 中采用的。

Redux 从 Event Sourcing 模式和状态机/转换系统中汲取灵感。谷歌搜索发现了经常使用的术语 state transition

<块引用>

状态转换可以表示为接受状态和事件并产生新状态的函数

但是我们怎么称呼这些“功能”呢?

恕我直言,reducer 是一个糟糕的名字,因为它没有编码任何关于其目的的含义,只是它的样子,但我想不出更好的名字。

最后,事物的所有名称都是在某个时候发明的,鉴于它的流行,reducer 现在与 state reducerstate transition function 相关联。

例如查看术语 argument 的起源:

<块引用>

“论证”一词的使用源自天文学,天文学在历史上使用表格来根据行星在天空中的位置来确定行星的空间位置。这些表格是根据称为参数的测量角度组织的,字面意思是“阐明其他事物的东西”。

答案 7 :(得分:0)

这不是一个很具体的名字,但是 Redux 中的 reducer 和其他 reducer 函数做的事情是一样的。他们吸收了多种东西,然后还给我们一件东西。 Redux reducer 接受两件事(前一个状态和要在前一个状态上执行的操作)并将这两件事归结为一个:下一个状态。

答案 8 :(得分:0)

调用 Redux reducers reducers 在语义上是不正确的并且没有多大意义。 这就是作者困惑的原因。

reducer 是一个函数,它将一组值减少为单个值
我们也可以说它折叠值 - 因此是函数式编程中的经典 fold() fn。

由于 Redux reducer 折叠一组值,而是将一个动作应用于一个状态并且总是返回相同的形状(State -> Action -> State) - 它应该被称为 applicator applier
但是,由于我们必须始终返回相同的状态形状,而不仅仅是完全不相关的 - 我们将 Redux 状态应用程序称为 changers、< em>transformers 或 mutators
而且,使用诸如'mutate the state''state mutator' 之类的术语确实已经司空见惯。

但 Redux 听起来比 AppluxMutux 酷多了 :)

相关问题