"Undefined" 尝试在 Javascript 中重现功能管道时

"Undefined" when trying to reproduce a functional pipe in Javascript

我今天刚开始学习函数式编程,我正在尝试使用管道创建一个示例:

const pipe = (...fns) => x => fns.reduce((acc, f) => f(acc), x)
const buffs = [{ power: 5, type: 'SPEED' }, { power: 2, type: 'SPEED' }]
let pos = { x: 0, y: 0 }

const addToPos = pos => amount => ({ ...pos, x: pos.x + amount })
const add1ToPos = pos => addToPos(pos)(1)
const add2ToPos = pos => addToPos(pos)(2)
const addAll = pos => pipe(
  add1ToPos,
  add2ToPos,
)(pos)

pos = addAll(pos)
console.log(pos) // returns { x: 3, y: 0 } as expected

但是,当我尝试从 buffs 添加所有这些 power 到我的管道时,如下所示:

const addAll = pos => pipe(
  add1ToPos,
  add2ToPos,
  addToPos(buffs.reduce((a, b) => a + b.power, 0))
)(pos)

我明白了

{ x: undefined[object Object] }

我真的不知道为什么会这样,因为它工作得很好:

const numbers = [{ num: 4, color: 'blue' }, { num: 5, color: 'red' }]
let total = 0

const addToTotal = total => amount => total + amount
const add1ToTotal = total => addToTotal(total)(1)

const addAll = total => pipe(
  add1ToTotal,
  addToTotal(numbers.reduce((a, b) => a + b.num, 0))
)(total)

total = addAll(total) // returns 10 as expected

我做错了什么?

我也是新手,但我想知道,为什么不将 const addToPos = pos => amount => ({ ...pos, x: pos.x + amount }) 重写为 const addToPos = amount => pos => ({ ...pos, x: pos.x + amount })

const pipe = (...fns) => x => fns.reduce((acc, f) => f(acc), x)
const buffs = [{ power: 5, type: 'SPEED' }, { power: 2, type: 'SPEED' }]
let pos = { x: 0, y: 0 }

const addToPos = amount => pos => ({ ...pos, x: pos.x + amount })
const add1ToPos = addToPos(1)
const add2ToPos = addToPos(2)
const addAll = pos => pipe(
  add1ToPos,
  add2ToPos,
  addToPos(buffs.reduce((a, b) => a + b.power, 0)),
)(pos)

pos = addAll(pos)
console.log(pos)

尝试更详细地解释为什么最后一个示例的 addAll 有效而第一个示例无效(抱歉,有点冗长):

所以首先,最后一个示例的 addAll 有点错误。让我们看看 addToTotal:

const addToTotal = total => amount => total + amount

如果你调用 addToTotal(4) 你得到的是一个看起来像 amount => 4 + amount 的新函数,请注意这里我们分配了 4 给总参数而不是数量参数,当我们可能希望留下 total => total + 4,因为管道操作正试图将值添加到总数中。那么它为什么有效呢?加法运算的顺序无关紧要,amount => 4 + amounttotal => total + 4 将始终产生相同的结果。

那么为什么第一个示例不起作用?

在第一个示例中,addAll 将一个 pos 对象传递给管道中的每个函数。最后一个函数是addToPos(buffs.reduce((a, b) => a + b.power, 0)),什么是addToPos(buffs.reduce((a, b) => a + b.power, 0))?好吧,如果我们想象 buffs.reduce((a, b) => a + b.power, 0) 是 5,它应该等同于:

addToPos(5)

这将是:

amount => ({ ...5, x: 5.x + amount })

当我们真正想要的是一个以 pos 作为参数的函数时;类似于:

pos => ({ ...pos, x: pos.x + 5 })

如果我们在调用链中交换 pos 和 amount(不确定这是不是正确的术语),我们得到:

addToPos = amount => pos => ({ ...pos, x: pos.x + amount })

如果我们对其应用金额,我们将得到一个将 pos 对象作为参数的函数。

如果函数是手写的,可能会更容易看到这些东西;原始 addToPos:

function addToPos(pos) {
  return function(amount) {
    return { ...pos, x: pos.x + amount };
  }
}

addToPos 即returns一个以pos为参数的函数:

function addToPos(amount) {
  return function(pos) {
    return { ...pos, x: pos.x + amount };
  }
}