Function programming paradigm treats functions as its first class citizens which means
- you can pass a function as an argument to another function
- you can return a function from a function and
- you can compose a function inside another function and assign it to a variable too just as we do with any other value or reference type.
To build up this concept, consider a summation function which gives sum of elements in the range a to b.
This can be written recursively as follows.
def summation(a: Int, b: Int): Int = { if (a > b) 0 else a + summation(a + 1, b) } //> summation: (a: Int, b: Int)Int summation(3, 6) //> res0: Int = 18
There are 2 problems with the above piece of code.
Note that you must always specify the return type of the recursive function, otherwise the Scala compiler will not be able to infer the type, as while doing the type inference it will end into a never ending recursive calls.
Taking this a step ahead, the requirement might be to
where f can be
f(x) = x*x
f(x) = x*x*x
f(x) = factorial(x)
So the summation function will take one more argument f i.e. the function to be applied on each a before adding it to the accumulator. The function f takes an Int argument and returns an Int value. Hence the summation function can be redefined as,
Summation of factorial of numbers between a to b - Define a function fact in tail recursive way to find factorial of a number and then pass it to summation as an argument.
Lambda expressions - For small functions, like square, cube of a number we can define the functions inline as lambda expressions while making summation function call.
Currying the function - In Currying, a function takes only one argument and returns a value. Let's apply some currying on summation function named as summation1.
The advantage is that we can create different flavors of summation function and then call them with proper arguments when required. The illustration is as follows.
Taking currying to the advanced level - Rather than having a sub function being returned by a function, we can logically aggregate the parameters into the same function by passing them in separate groups. This will help in reusing the function.
Q. Can we have a series function which will do operation x (instead of only summation) and at the same time apply a function (e.g. square, cube, factorial) on the element before adding it to the result? Most importantly can it be designed in a reusable fashion?
The answer is yes. Following is the series function where Op is a function which takes two arguments and returns an integer value. Op can be summation, product or any other operation. The acc value can be 0 for summation or 1 for product which represents the unit value with respect to the operation.
The function can be used to do summation of squares of numbers between 1 to 5.
The function can be used to define factorial function.
- The parameter b doesn't change, still I have to pass it to all the recursive calls as summation requires 2 parameters.
- The summation function is not tail recursive. If the range of a and b is huge then the piece of code may result into stack overflow.
def summation(a: Int, b: Int): Int = { def loop(a: Int, acc: Int): Int = {
if (a > b) acc else loop(a + 1, a + acc)
} loop(a,0); } //> summation: (a: Int, b: Int)Int summation(3, 6) //> res0: Int = 18
Note that you must always specify the return type of the recursive function, otherwise the Scala compiler will not be able to infer the type, as while doing the type inference it will end into a never ending recursive calls.
Taking this a step ahead, the requirement might be to
- find sum of squares of numbers between a and b
- find sum of cubes of numbers between a and b
- find sum of factorials of numbers between a and b
where f can be
f(x) = x*x
f(x) = x*x*x
f(x) = factorial(x)
So the summation function will take one more argument f i.e. the function to be applied on each a before adding it to the accumulator. The function f takes an Int argument and returns an Int value. Hence the summation function can be redefined as,
def summation(a: Int, b: Int, f: Int => Int): Int = { def loop(a: Int, acc: Int): Int = if (a > b) acc else loop(a + 1, f(a) + acc) loop(a, 0); } //> summation: (a: Int, b: Int, f: Int => Int)Int
Summation of factorial of numbers between a to b - Define a function fact in tail recursive way to find factorial of a number and then pass it to summation as an argument.
def fact(a: Int): Int = { def f(a: Int, acc: Int): Int = if (a == 0) acc else f(a - 1, a * acc) f(a, 1) } //> fact: (a: Int)Int summation(3, 5, fact) //> res0: Int = 150
Lambda expressions - For small functions, like square, cube of a number we can define the functions inline as lambda expressions while making summation function call.
summation(1,5,x=>x*x) //> res1: Int = 55 summation(1,3,x=>x*x*x) //> res2: Int = 36
Currying the function - In Currying, a function takes only one argument and returns a value. Let's apply some currying on summation function named as summation1.
def summation1(f:Int=>Int):((Int,Int)=>Int) = { def FSum(a:Int, b:Int):Int = { def loop(a:Int, acc:Int):Int = { if(a>b) acc else loop(a+1,f(a)+acc) } loop(a,0) } FSum }
The advantage is that we can create different flavors of summation function and then call them with proper arguments when required. The illustration is as follows.
def sqrSummation = summation1 (x=>x*x) //> sqrSummation: => (Int, Int) => Int sqrSummation(5,7) //> res1: Int = 110 def cubicSummation = summation1 (x=>x*x*x)//> cubicSummation: => (Int, Int) => Int sqrSummation(1,3) //> res2: Int = 14 def factoSummation = summation1(fact) //> factoSummation: => (Int, Int) => Int factoSummation(3,5) //> res3: Int = 150 summation1(x=>x)(1,10) //> res4: Int = 55
Taking currying to the advanced level - Rather than having a sub function being returned by a function, we can logically aggregate the parameters into the same function by passing them in separate groups. This will help in reusing the function.
Q. Can we have a series function which will do operation x (instead of only summation) and at the same time apply a function (e.g. square, cube, factorial) on the element before adding it to the result? Most importantly can it be designed in a reusable fashion?
The answer is yes. Following is the series function where Op is a function which takes two arguments and returns an integer value. Op can be summation, product or any other operation. The acc value can be 0 for summation or 1 for product which represents the unit value with respect to the operation.
def series(Op: (Int, Int) => Int, acc: Int)(f: Int => Int)(a: Int, b: Int): Int = if (a > b) acc else series(Op, Op(f(a), acc))(f)(a + 1, b)
The function can be used to do summation of squares of numbers between 1 to 5.
series((x,y)=>x+y,0)(x=>x*x)(1,5) //> res0: Int = 55
The function can be used to define factorial function.
def fact (n:Int):Int = series((x,y)=>x*y,1)(x=>x)(1,n) //> fact: (n: Int)Int fact(5) //> res1: Int = 120
No comments:
Post a Comment
Your comments are very much valuable for us. Thanks for giving your precious time.