首页 > 解决方案 > 减少函数中的变异累加器是否被认为是不好的做法?

问题描述

我是函数式编程的新手,我正在尝试重写一些代码以使其更具功能性来掌握这些概念。刚才我发现了Array.reduce()函数并用它来创建组合数组的对象(在那之前我使用过for循环)。但是,我不确定。看看这段代码:

const sortedCombinations = combinations.reduce(
    (accum, comb) => {
        if(accum[comb.strength]) {
            accum[comb.strength].push(comb);
        } else {
            accum[comb.strength] = [comb];
        }

        return accum;
    },
    {}
);

显然,这个函数改变了它的参数accum,所以它不被认为是纯的。另一方面,如果我理解正确,reduce 函数会从每次迭代中丢弃累加器,并且在调用回调函数后不使用它。不过,它不是一个纯函数。我可以这样重写它:

const sortedCombinations = combinations.reduce(
    (accum, comb) => {
        const tempAccum = Object.assign({}, accum);
        if(tempAccum[comb.strength]) {
            tempAccum[comb.strength].push(comb);
        } else {
            tempAccum[comb.strength] = [comb];
        }

        return tempAccum;
    },
    {}
);

现在,在我的理解中,这个函数被认为是纯粹的。但是,它每次迭代都会创建一个新对象,这会消耗一些时间,而且显然还需要内存。

所以问题是:哪种变体更好,为什么?纯度真的那么重要,以至于我应该牺牲性能和内存来实现它吗?或者也许我错过了一些东西,还有更好的选择?

标签: javascriptfunctional-programming

解决方案


TL; DR:如果您拥有蓄电池,则不是。


在 JavaScript 中使用扩展运算符来创建漂亮的单行归约函数是很常见的。开发人员经常声称它还使他们的功能在过程中变得纯粹。

const foo = xs => xs.reduce((acc, x) => ({...acc, [x.a]: x}), {});
//------------------------------------------------------------^
//                                                   (initial acc value)

但是让我们想一想……如果你发生了变异,可能会出什么问题acc?例如,

const foo = xs => xs.reduce((acc, x) => {
  acc[x.a] = x;
  return acc;
}, {});

绝对没有。

的初始值acc是动态创建的空文字对象。在这一点上,使用扩展运算符只是一种“修饰”的选择。这两个函数都是纯函数。

不变性是一种特性,而不是过程本身。这意味着克隆数据以实现不变性很可能是一种幼稚且低效的方法。大多数人忘记了传播运算符无论如何只做一个浅克隆!

我不久前写了这篇文章,我声称变异和函数式编程不必相互排斥,并且我还表明使用扩展运算符并不是一个简单的选择。


推荐阅读