【问题标题】:How to convert this function into a pure function?如何将此函数转换为纯函数?
【发布时间】:2020-06-07 07:24:38
【问题描述】:

我有以下功能。

const array = [1, 2, 3];

// Impure function

function addElementToArray(element) {
  array.push(element);
}

这是一个不纯函数,因为它会改变全局数组。所以,我认为将整个数组作为函数参数将使函数变得纯粹。

function addElement(array, element) {
  array.push(element);
}

但是,我发现它也有副作用。

那么,让它成为纯函数的最佳方法是什么?

【问题讨论】:

  • 最好的方法是使用函数式数据结构,例如列表。然后你可以将一个新元素 cons 到列表中,这是一个常数时间的操作。
  • @AaditMShah OP 正在寻找纯函数实现;这仍然不是纯粹的,因为它仍然会改变输入,无论复杂性如何。
  • @avocadatoria 不,它不会改变输入。它将构造一个新列表,其中新元素作为头部,旧列表作为尾部。函数式数据结构是不可变的。
  • 不清楚这里的目标是什么。使函数成为纯函数的最简单方法是删除所有具有不良副作用的语句,因此function addElementToArray(element) {}。那么你到底想用数组做什么呢?这是一个重要的问题,答案将指导您了解所需的功能和数据结构。
  • @AaditMShah 知道了;我误解了你的建议。

标签: javascript function ecmascript-6 functional-programming


【解决方案1】:

在函数体中,您可以复制输入数组并将元素附加到新数组的末尾。您可以使用 es6 扩展运算符轻松做到这一点

function addElementIntoArray(a, element) {
  // returning a brand new array
  return [...a, element];
}

这样,它不会改变全局数组,函数将是一个纯函数。

【讨论】:

    【解决方案2】:

    为每个操作复制数组是非常低效的。或者,您可以将不纯推送操作推迟到运行时(在下面的草图中称为 main),并且每个组合只复制一次数组:

    /*** pure ***/
    
    const Defer = thunk => ({get runDefer() {return thunk()}});
    
    const defMap = f => tx =>
      Defer(() => f(tx.runDefer));
    
    const pipe = g => f => x => f(g(x));
    
    const catDef = xs => ys =>
      Defer(() => xs.concat(ys)); // pass [] to copy the array
    
    const pushDef = x => tx =>
      defMap(xs => (xs.push(x), xs)) (tx);
    
    const xs = ["foo", "bar"];
    
    const main = pipe(
      pushDef("baz"))
       (pushDef("bat"))
        (catDef(xs) ([]));
    
    /*** impure ***/
    
    console.log(
      main.runDefer); // runs the side effect within the composition only at runtime
    
    console.log(xs); // no global side effect

    【讨论】:

      【解决方案3】:

      使用 push 方法,您可以简单地在函数内部制作一个数组的本地副本,将项目推送到复制的数组中,然后最终返回该数组。 这是代码。

      function addNewelement(array,item){
          const newArr = array.slice();     
          newArr.push(item);
          return newArr;
      }
      

      现在,它不会改变全局数组。

      【讨论】:

        【解决方案4】:

        最简单的解决方案是使用purely functional data structure,例如列表。

        // empty :: List a -- an empty list of any type
        const empty = null;
        
        // cons :: (a, List a) -> List a -- add an element to the list
        const cons = (head, tail) => ({ head, tail });
        
        // toArray :: List a -> [a] -- convert the list to an array in reverse
        const toArray = list => {
            if (list === empty) return [];
            const array = toArray(list.tail);
            array.push(list.head); // although we use mutation here, yet toArray is pure
            return array;
        };
        
        // example :: List Int
        const example = cons(3, cons(2, cons(1, empty)));
        
        // [1, 2, 3] :: [Int]
        console.log(toArray(example));

        请注意,虽然toArray 函数使用了变异,但它是纯粹的,因为:

        1. 它为相同的输入返回相同的输出。
        2. 它没有任何副作用。

        我已经准确解释了在earlier answer 中将函数视为纯函数意味着什么。

        【讨论】:

          猜你喜欢
          • 2021-08-01
          • 2016-01-01
          • 1970-01-01
          • 2020-09-03
          • 2020-07-22
          • 2020-10-10
          • 2021-11-03
          • 2020-08-20
          相关资源
          最近更新 更多