Function returning higher order function For the below given exercise:


  Define a function cycle which takes in three functions as arguments: f1, f2, f3. cycle will then return another function. The returned fuction should take in an integer argument n and do the following:
  
  return a function that takes in an argument x and does the following:
  
  if n is 0, just return x
  
  if n is 1, apply the first function that is passed to cycle to x
  
  if n is 2, the first function passed to cycle is applied to x, and then the second function passed to cycle is applied to the result of that (i.e. f2(f1(x))).
  
  If n is 3, apply the first, then the second, then the third function (i.e. f3(f2(f1(x))))
  
  if n is 4, apply the first, then the second, then the third, then the first function (i.e. f1(f3(f2(f1(x)))))
  
  And so forth
  
  Hint: most of the work goes inside the most nested function.

def cycle(f1, f2, f3):
    """ Returns a function that is itself a higher order function 
    &gt;&gt;&gt; add1 = lambda x: x+1 
    &gt;&gt;&gt; times2 = lambda x: 2*x 
    &gt;&gt;&gt; add3 = lambda x: x+3 
    &gt;&gt;&gt; my_cycle = cycle(add1, times2, add3)
    &gt;&gt;&gt; identity = my_cycle(0)
    &gt;&gt;&gt; identity(5)
    5
    &gt;&gt;&gt; add_one_then_double = my_cycle(2)
    &gt;&gt;&gt; add_one_then_double(1) # semanitcally the same as times2(add1(1))
    4
    &gt;&gt;&gt; do_all_functions = my_cycle(3)
    &gt;&gt;&gt; do_all_functions(2) # semantically the same as add3(times2(add1(2)))
    9
    &gt;&gt;&gt; do_more_than_a_cycle = my_cycle(4)
    &gt;&gt;&gt; do_more_than_a_cycle(2) # semantically the same as add1(add3(times2(add1(2))))
    10
    &gt;&gt;&gt; do_two_cycles = my_cycle(6) # semantically the same as add3(times2(add1(add3(times2(add1(1))))))
    &gt;&gt;&gt; do_two_cycles(1)
    19
    """

    " *** YOUR CODE HERE *** "



Here is the solution:

def cycle(f1, f2, f3):
    """ Returns a function that is itself a higher order function 
    &gt;&gt;&gt; add1 = lambda x: x+1 
    &gt;&gt;&gt; times2 = lambda x: 2*x 
    &gt;&gt;&gt; add3 = lambda x: x+3 
    &gt;&gt;&gt; my_cycle = cycle(add1, times2, add3)
    &gt;&gt;&gt; identity = my_cycle(0)
    &gt;&gt;&gt; identity(5)
    5
    &gt;&gt;&gt; add_one_then_double = my_cycle(2)
    &gt;&gt;&gt; add_one_then_double(1) # semanitcally the same as times2(add1(1))
    4
    &gt;&gt;&gt; do_all_functions = my_cycle(3)
    &gt;&gt;&gt; do_all_functions(2) # semantically the same as add3(times2(add1(2)))
    9
    &gt;&gt;&gt; do_more_than_a_cycle = my_cycle(4)
    &gt;&gt;&gt; do_more_than_a_cycle(2) # semantically the same as add1(add3(times2(add1(2))))
    10
    &gt;&gt;&gt; do_two_cycles = my_cycle(6) # semantically the same as add3(times2(add1(add3(times2(add1(1))))))
    &gt;&gt;&gt; do_two_cycles(1)
    19
    """
    def f(n):
        tple = (f1, f2, f3)
        def g(x, count = n):
            if count == 0:
                return x
            else:
                return g(tple[(n-count)%3](x), count-1)
        return g
    return f


My question:


The above solution is written in the functional paradigm. Please let me know if the code style breaks the functional paradigm.
Can the code be optimized without default valued second parameter of function g and tuple tple?


Note: In python, I do not like lambda and decorators
That's a pretty good solution to a tricky exercise.  I recommend two simplifications:


The definition of tple can be lifted out of f into cycle itself, as it never changes.
The code would be simpler if you count up instead of down.




def cycle(f1, f2, f3):
    tple = (f1, f2, f3)
    def f(n):
        def g(x, count = 0):
            if count == n:
                return x
            else:
                return g(tple[count % 3](x), count + 1)
        return g
    return f


As you observed, your g function is a leaky abstraction: it is supposed to take just an x argument, but it's possible to interfere with its proper behaviour by also passing it a count.  Here is one solution that avoids that problem ("apply f1, then move f1 to the end of the queue").

def cycle(f1, f2, f3):
    def f(n):
        def g(x):
            if n == 0:
                return x
            else:
                return cycle(f2, f3, f1)(n - 1)(f1(x))
        return g
    return f