0

I'm new to functional programming and typescript. I was told to only use recursion so for loops aren't allowed. How do I call a function n number of times with recursion?

Here's the imperative pseudo code

for i <- 0 to n:
    f(x) //function call

I tried this:

function loop(n:number, f:()=>number):number [
    return n > 0? loop(n-1, f) : f;
}

but I realised that this only returns f when n = 1. What I want is to call f n number of times. May I know how could this be done?

llamaro25
  • 500
  • 3
  • 15
  • `I realised that this only returns f when n = 1` So what happens when `n > 1`? – Kenneth K. Sep 06 '19 at 13:59
  • What are you supposed to do with the results of the function calls? Just discarding them, like the imperative code does, is meaningless in functional programming. And where should the argument (`x` in your imperative code) come from? – Bergi Sep 06 '19 at 13:59
  • @Bergi the results are discarded but right now the function is only called once instead of 4 times – llamaro25 Sep 06 '19 at 14:01
  • @KennethK. right now it's calling itself for recursion but how can I call itself recursively and call another function at the same time? – llamaro25 Sep 06 '19 at 14:02
  • 1
    @llamaro25 In the code you have written, you've typed `f` as a `number`, but it should be a function. – Richiban Sep 06 '19 at 14:03
  • 1
    @llamaro25 In your `loop`, `f()` is called *never*? Maybe also show us how you are invoking `loop`. – Bergi Sep 06 '19 at 14:05
  • @Richiban sorry, I'm still new to typescript. I have edited it, is it correct now? – llamaro25 Sep 06 '19 at 14:06
  • If the results of a function call can be discarded, the whole function call can be discarded, and calling the function not once has the same result - none - as calling it a thousand times. So knowing what should happen with the results is essential in functional programming. Please tell us what results you need, or what problem you are trying to solve, and show us an example for the function `f` that you want to pass. – Bergi Sep 06 '19 at 14:08
  • @llamaro25 No, it's not correct now. It doesn't typecheck. Doesn't your TypeScript compiler show you that? – Bergi Sep 06 '19 at 14:11
  • If you're just calling a function for its side-effects you might as well just use a for loop since it's inherently imperative. If you really want to use recursion you could do something like `function loop(n, f) { if (n > 0) { f(); loop(n-1, f) } }`. – Lee Sep 06 '19 at 14:17
  • You can either do this lazily with a generator `iterate = f => function* (x) {while (true) yield x = f(x)}` or you must accumulate the results with an `Array`. The latter requires that you determine the length of the `Array` and hence the number of iterations upfront. – scriptum Sep 06 '19 at 14:29
  • Don't be too dogmatic about forbidding for or while loops. In the end, looping has to be implemented somehow and you could even hide away the while or for loop behind a functional interface. – k0pernikus Sep 06 '19 at 14:33
  • Possible duplicate of [How do I replace while loops with a functional programming alternative without tail call optimization?](https://stackoverflow.com/questions/43592016/how-do-i-replace-while-loops-with-a-functional-programming-alternative-without-t) – k0pernikus Sep 06 '19 at 14:34

2 Answers2

0

The simplest solution with recursion:

// Your function
functionName = () => {
  console.log('functionName was called');
}

// Caller function
callFunctionNTimes = n => {
    if(n && n > 0) {
      functionName();
    return callFunctionNTimes(n - 1);
  }
}

// Use
callFunctionNTimes(2);

Output:

functionName was called
functionName was called

Simplified syntax:

callFunctionNTimes = n => {
    const recursionCondition = n && n > 0;
    recursionCondition && functionName()
    recursionCondition && callFunctionNTimes(n - 1);
}

High order function:

functionName = () => {
  console.log('functionName was called');
}

callFunctionNTimes = (functionForExecute) => n => {
    const recursionCondition = n && n > 0;
    recursionCondition && functionForExecute();
    recursionCondition && callFunctionNTimes(functionForExecute)(n - 1);
}

callFunctionNTimes(functionName)(2);
Jackkobec
  • 3,665
  • 23
  • 22
  • `&&` is not "simplified syntax", it's *minified* syntax at best. It's confusing and abuse of the operator. – Bergi Sep 07 '19 at 02:19
  • Minimalism is a JavaScript paradigm, isn't it? – Jackkobec Sep 07 '19 at 02:24
  • In the sense that JavaScript is a multi-paradigm language, maybe? But when being asked to write functional code, that means writing *declarative* code. – Bergi Sep 07 '19 at 02:29
  • Right you are. I've written a little poem: We don't need a stupid 'impl', Just relax and keep it simple:) – Jackkobec Sep 07 '19 at 02:34
0

generics

I'd probably write it using generics, such as T below -

function hello (name: string): string {
  return `hello ${name}`;
}

function loop<T>(n: number, f: (x: T) => T, x: T): T {
  return n > 0 ? loop(n-1, f, f(x)) : x;
}

console.log(loop(3, hello, "world"));
// hello hello hello world

Which compiles to -

"use strict";
function hello(name) {
    return `hello ${name}`;
}
function loop(n, f, x) {
    return n > 0 ? loop(n - 1, f, f(x)) : x;
}
console.log(loop(3, hello, "world"));
// hello hello hello world

don't blow the stack

Direct recursion can cause a stack-overflow in JavaScript. If you want to loop a function thousands of times, you will need to convert it to a while loop one way or another -

Here's a simple way -

function hello (name: string): string {
  return `hello ${name}`
}

function loop<T>(n: number, f: (x: T) => T, x: T): T {
  while (n-- > 0) x = f(x)
  return x
}

console.log(loop(20000, hello, "world"));
// hello hello hello ... hello world

Which compiles to -

"use strict";
function hello(name) {
    return `hello ${name}`;
}
function loop(n, f, x) {
    while (n-- > 0)
        x = f(x);
    return x;
}
console.log(loop(20000, hello, "world"));
// hello hello hello ... hello world
Thank you
  • 107,507
  • 28
  • 191
  • 224