Last month, I was interviewed by InfinitelyBeta recruitment team. Interview was really cool, and I really enjoyed every part of it. One of the tasks given to me was to implement a simple function called reduce with the following characteristics:

  • first parameter of reduce should be name of a function, defined earlier.
  • second parameter should be a list of arguments, which is to be passed to the first parameter.
  • should return the result of executing first parameter

for eg:

 

1
2
3
4
5
def add(a, b):
    return a+b
'''
reduce function should called as : reduce(add, [1,2]) and should return 3
'''

Let’s see how to write a function, which will call another function using the parameters specified.

In python, we can write a function as follows:
Container Function

1
2
3
4
5
6
7
# container function will call the function func with the parameter args
def container(func, args):
    func(args)
 
# generic function to print
def printer(a):
    print a

If we call

1
container(printer, "hello")

it will print hello. If we try to pass the second parameter as a list, for eg:

1
container(printer, [1, 2, "hello"])

,it will just print the same list for you. (Remember that print function will just print the complete list when a list is passed to it as argument).

Now, let’s try something more deeper into this container function. Redefining the container function as follows :

1
2
def container(func, *args): #line 1
    return func(*args) #line 2
  • RULE 1 : asterisk before args in the first line means to “take the rest of the parameters given and put them in a list called args.”
  • RULE 2 : asterisk before args in the second line mean “take this list called args and unwrap it into the rest of the parameters.”

Now, let’s see what happens:

Python 2.6.1 (r261:67515, Jun 24 2010, 21:47:49) 
[GCC 4.2.1 (Apple Inc. build 5646)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> def container(fun, *args):
...     return fun(*args)
... 
>>> def add(a, b):
...     return a+b
... 
>>> container(add, 1,2)
3

Here, we got 3 as output, which is correct. How?

When we called the container function with add, 1, 2 as arguments, 1, 2 where added to the tuple args (see RULE 1). Thus, in container function, func => add and args = [1, 2]. Then, according to RULE 2, the tuple args was expanded into parameters for add and add(1, 2) was called. We could easily find the values in args as:

>>> def container(func, *args):
...     print args
...
>>> container(add, 1,2)
(1, 2)
>>>

Now see what happens when we try to pass a list as the argument to second parameter:

>>> container(add, [1, 2])
Traceback (most recent call last):
  File "", line 1, in 
  File "", line 2, in container
TypeError: add() takes exactly 2 arguments (1 given)
>>>

This time, the entire list [1, 2] was taken as a single argument. We may solve this issue by making a minor change to our container function as follows:

>>> def container(fun, args):
...     return fun(*args)
... 
>>> container(add, [1,2])
3
>>>

Here, we’ve specified that container will take exactly 2 arguments, and the second argument args should be unwraped into a list of parameters.

So, what if we need to call the container function as follows?

container(function-name, [1,2], [4, 5])

Here we have 3 parameters, in which second and third are lists. We shall define the container function as:

>>> def container(fun, *args):
...     return fun(*args)
>>>
>>>
>>> container(add, [1,2], [4,5])
[1, 2, 4, 5]

Now, we have enough background to implement the reduce function. I was asked to do it for the add function, so, I did it as follows:

def reduce(fun, args):
    k = args[0]
    if len(args) == 2:
        k = fun(arg[0], arg[1])
        return k  
    return k + reduce(fun, args[1:])

Since this was only for add, I was asked to make it generic for any function. After some corrections to it, I changed it as follows:

Reduce function

1
2
3
4
5
6
def reduce(fun, args):
    k = args[0]
    if len(args) == 2:
        k = fun(arg[0], arg[1])
        return k  
    return fun(k, reduce(fun, args[1:]))


Tail recursive implementation

1
2
3
4
5
def reduce(fun, args):
    if len(args)==1:
        return args[0]
    args.insert(args[2:], fun(args[0],args[1])
    return reduce(fun, args )

Bottom line:
Infinitely Beta recruited me, and received the offer letter 2 days after my interview :)