Uğurcan Şengit
Uğurcan Şengit

Reputation: 1026

How to curry a recursive function in ES6

I was doing some functional programming practice with javascript lambda expressions and came up with the following code. The code aims to return a function that calculates given number's power of given base value. My code as follows;

const findUpper = base => (num, i = 0) => {
  if (num < base) {
    return i;
  } else {
    i++;
    return findUpper(Math.round(num / base), i);
  }
}

const findUpper2 = findUpper(2)
console.log(findUpper2(8)) //expected to have 3

The problem here is, recursion becomes broken after first findUpper call, because it returns a function.

How can I make this snippet work?

Upvotes: 0

Views: 1105

Answers (2)

Mulan
Mulan

Reputation: 135227

Here's your mistake

i++
return findUpper(Math.round(num / base), i);

You intended:

// don't forget, findUpper is a curried function
return findUpper(Math.round(num/base))(i + 1);

Here's another way to write your program as a pure expression

const findUpper = (base = 1) => (num = 0, exp = 0) =>
  num < base
    ? exp
    : findUpper (base) (num / base, exp + 1)
    
console.log (findUpper (2) (8))

Using generic loop/recur procedures, we keep findUpper's parameters clean, increase loop performance, and maintain the curried interface you want

const recur = (...values) =>
  ({ recur, values })
  
const loop = f =>
{
  let acc = f ()
  while (acc && acc.recur === recur)
    acc = f (...acc.values)
  return acc
}

const findUpper = (base = 1) => (num = 0) =>
  loop ((n = num, exp = 0) =>
    n < base
      ? exp
      : recur (n / base, exp + 1))

console.log (findUpper (2) (8))

Upvotes: 0

Jaromanda X
Jaromanda X

Reputation: 1

One way is like this

    var findUpper = base => {
        let fn = (num, i = 0) => {
            if (num < base) {
                return i;
            } else {
                i++;
                return fn(Math.round(num / base), i);
            }
        };
        return fn;
    }
    var findUpper2 = findUpper(2)
    console.log(findUpper2(8))

Declare the recursive function with a name inside fundUpper ... recursively call fn rather than findUpper

it's slightly "tidier" without using arrow functions needlessly

var findUpper = base => {
    return function fn(num, i = 0) {
        if (num < base) {
            return i;
        } else {
            i++;
            return fn(Math.round(num / base), i);
        }
    };
}
var findUpper2 = findUpper(2)
console.log(findUpper2(8))

Although, using => the code can be as simple as

const findUpper = base => {
    let fn = (num, i = 0) => (num < base) ? i : fn(Math.round(num / base), i + 1);
    return fn;
}

Upvotes: 3

Related Questions