简体   繁体   中英

Functional Programming: Calling a Curried Function

I'm implementing the game Tic Tac Toe / Naughts and Crosses in a functional programming style and have stumbled across a hurdle with curried functions.

I have a reoccurring pattern of functions in the form func(width, height, index) which I then wish to curry, binding width and height and leaving curriedFunc(index) .

However the problem arises when I have functions that expect one of these curried functions to be defined at compile-time.

They cannot be defined at compile time, because they need input from the user to then bind the values to the function.

Below is some example code of the pattern I've encountered.

// 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();

The only way I can think of solving this is to pass the curried function as an argument (to nextIndexInRowGivenWidth() in this example).

However I don't think this is ideal as if a function requires a few similarly curried functions at run-time, it quickly becomes unwieldy to define and curry said function.

The ideal solution would be if I could somehow make the binding of the values dynamic, suppose I could put the declaration getRowNum = getRowNumGivenWidth(width); before main() . This way I could call something like getRowNum(someInt) to initialise getRowNum() which I could then use in other functions that are already expecting it to be defined.

As this is a reoccurring pattern in my code, I was wondering if there is a design pattern to achieve this.

I think you are looking for

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

  // ...
}

Alternatively, you could define that nextIndexInRowGiven… function not with the width as the first curried parameter, but with getRowNum itself as the parameter:

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

  // ...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM