函数式编程:调用 Curried 函数

Functional Programming: Calling a Curried Function

我正在以函数式编程风格实现 Tic Tac Toe/Naughts and Crosses 游戏,并且偶然发现了一个障碍咖喱函数。

我有一个形式为 func(width, height, index) 的重复出现的函数模式,然后我想柯里化,绑定 widthheight 并留下 curriedFunc(index).

然而,当我的函数期望在编译时定义这些柯里化函数之一时,问题就出现了。

它们不能在编译时定义,因为它们需要用户输入然后将值绑定到函数。

下面是我遇到的模式的一些示例代码。

// Board indexes:
//  0 | 1 | 2
// ---+---+---
//  3 | 4 | 5
// ---+---+---
//  6 | 7 | 8

const getRowNumGivenWidth = w => i => Math.floor(i/w);

// I want to be able to declare nextIndexInRowGivenWidth() here, outside of main()
// but getRowNum() needs to be defined beforehand


const main = () => {

  // User input:
  const width = 3;

  // ...


  const getRowNum = getRowNumGivenWidth(width);

  const nextIndexInRowGivenWidth = width => currentIndex => {
    const rowNum = getRowNum(currentIndex);
    const nextIndex = currentIndex + 1;

    if (getRowNum(nextIndex) != rowNum)
      result = nextIndex - width;
    else
      result = nextIndex;

    return result;
  };


  const nextIndexInRow = nextIndexInRowGivenWidth(width);

  const board = [0, 1, 2, 3, 4, 5, 6, 7, 8];

  board.map(x => console.log(x, " -> ", nextIndexInRow(x)));

  // ...

}

main();

我能想到的解决这个问题的唯一方法是将柯里化函数作为参数传递(在本例中传递给 nextIndexInRowGivenWidth())。

但是我认为这并不理想,因为好像一个函数在 运行 时需要一些类似的 curried 函数,定义和 curry 所述函数很快变得笨拙。

理想的解决方案是,如果我能以某种方式动态绑定值,假设我可以将声明 getRowNum = getRowNumGivenWidth(width); 放在 main() 之前。这样我就可以调用 getRowNum(someInt) 之类的东西来初始化 getRowNum() 然后我可以在其他已经期望它被定义的函数中使用它。

由于这是我代码中重复出现的模式,我想知道是否有设计模式可以实现这一点。

我想你正在寻找

const getRowNumGivenWidth = w => i => Math.floor(i/w);

const nextIndexInRowGivenWidth = width => {
  const getRowNum = getRowNumGivenWidth(width);
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  return currentIndex => {
    const nextIndex = currentIndex + 1;

    if (getRowNum(nextIndex) != getRowNum(currentIndex))
      return nextIndex - width;
    else
      return nextIndex;
  };
};

const main = () => {
  // User input:
  const width = 3;
  const nextIndexInRow = nextIndexInRowGivenWidth(width);

  // ...
}

或者,您可以定义 nextIndexInRowGiven… 函数,而不是将 width 作为第一个柯里化参数,而是将 getRowNum 本身作为参数:

const getRowNumGivenWidth = w => i => Math.floor(i/w);

const nextIndexInRowGivenRowNumGetter = getRowNum => currentIndex => {
  const nextIndex = currentIndex + 1;

  if (getRowNum(nextIndex) != getRowNum(currentIndex))
    return nextIndex - width;
  else
    return nextIndex;
};

const main = () => {
  // User input:
  const width = 3;
  const nextIndexInRow = nextIndexInRowGivenRowNumGetter(getRowNumGivenWidth(width));

  // ...
}