I have a function in scala that I wonder if it's possible to make into a tail recursive function.
def get_f(f: Int => Int, x: Int, y: Int): Int = x match {
case 0 => y
case _ => f(get_f(f, x - 1, y))
}
I see that this function applies f function to result recursivly, x times. It's the same as applying it to y, x times. Also I suggest you to use if else instead of pattern matching.
#tailrec
def get_f(f: Int => Int, x: Int, y: Int): Int =
if(x == 0) y
else get_f(f, x - 1, f(y))
Add #tailrec annotation to ensure that it is tail recursive
It is possible but the way you've constructed it means you're going to have to use a Trampolined style to make it work:
import scala.util.control.TailCalls._
def get_f(f: Int => Int, x: Int, y: Int): TailRec[Int] = x match {
case 0 => done(y)
case _ => tailcall(get_f(f, x - 1, y)).map(f)
}
val answer = get_f(_+1, 0, 24).result
You can read about TailRec here or for more advanced study, this paper.
Let's start with reducing number of parameters from your non-tailrec version to make it clear what it actually does:
def get_f(f: Int => Int, x: Int, y: Int) = {
def get_f_impl(x: Int): Int = x match {
case 0 => y
case _ => f(get_f_impl(x - 1))
}
get_f_impl(x)
}
The idea is that actually you apply f-function x-times to initial value y. So, it becomes clear that you can do something like this in order to make it tail-recursive:
def get_f(f: Int => Int, x: Int, y: Int) = {
#tailrec def get_f_impl(acc: Int, x: Int): Int =
if (x == 0) acc else get_f_impl(f(acc), x - 1)
get_f_impl(y, x)
}
REPL investigation:
Your original implementation:
scala> get_f(_ + 1, 4, 0)
res6: Int = 4
Your implementation (with params optimisation):
scala> get_f(_ + 1, 4, 0)
res0: Int = 4
Tailrec implementation:
scala> get_f(_ + 1, 4, 0)
res3: Int = 4
P.S. For more complex cases trampolines might fit: https://espinhogr.github.io/scala/2015/07/12/trampolines-in-scala.html
P.S.2 You can also try:
Scala - compose function n times
#adamw noticed that it will allocate n-sized list, so might not be very efficient
Endo.mulitply in scalaz (your f: Int => Int is actually an endomorphism): https://stackoverflow.com/a/7530783/1809978 - not sure about efficiency
I'll add that you can achieve the same result by using foldLeft on Range, like this:
def get_f(f: Int => Int, x: Int, y: Int) =
(0 until x).foldLeft(y)((acc, _) => f(acc))
In line with previous responses
def get_f2( f: Int => Int, x: Int, y: Int) : Int = {
def tail(y: Int, x: Int)(f: Int => Int) : Int = {
x match {
case 0 => y
case _ => tail(f(y), x - 1)(f) : Int
}
}
tail(y, x)(f)
}
Related
def fact(n: Int) = products(x => x)(1,n)
fact(5)
def mapReduce(f: Int => Int, combine: (Int, Int) => Int, zero: Int)(a:Int,b:Int): Int =
def recur(a: Int): Int =
if(a>b) zero
else combine(f(a),recur(a+1))
recur(a)
def sum(f: Int => Int) = mapReduce(f, (x, y) => x+y, 0)
def products(f: Int => Int) = mapReduce(f, (x, y) => x*y, 1)
sum(fact)(1,5)
products(fact)(1,5)
def mapReduce(f: Int => Int, combine: (Int, Int) => Int, zero: Int)(a:Int,b:Int): Int = {
def recur(a: Int): Int =
if (a > b) zero
else combine(f(a), recur(a + 1))
recur(a)
}
def sum(f: Int => Int): (Int, Int) => Int = mapReduce(f, (x, y) => x+y, 0)
def products(f: Int => Int): (Int, Int) => Int = mapReduce(f, (x, y) => x*y, 1)
def fact(n: Int) = products(x => x)(1,n)
fact(5)
sum(fact)(1,5)
products(fact)(1,5)
I tried it using scala 2.13 on a scala worksheet, here curly brackets are needed for mapReduce as it contains two expressions one is a function definition and another a function invocation. Secondly when methods that returns functions such as sum and products the return type need to be specified. But I wasn't able to reproduce the error that you mentioned.
I'm almost sure there is already an answer, but being a beginner in Scala I cannot find it.
So, I've made a power function of two arguments:
def power(x: Double, n: Int): Double = {
#scala.annotation.tailrec
def go(acc: Double, i: Int): Double = {
if (i == 0) acc
else go(acc * x, i - 1)
}
go(1, n)
}
If I then calculate power(2, 3)
println(
power(2, 3)
)
I get 8.0, which is OK, but it would be better to have just 8 if the first argument is Int. How can I achieve that?
You can use Numeric
def power[T](x: T, n: Int)(implicit num: Numeric[T]): T = {
import num._
#scala.annotation.tailrec
def go(acc: T, i: Int): T = {
if (i == 0) acc
else go(acc * x, i - 1)
}
go(fromInt(1), n)
}
power(2, 3) // 8: Int
The magic of type class
The syntax of currying in scala is for example
def f(x: Int, b: Int) = x + y
is
def f(x: Int)(b: Int) = x + y
And currying for sum to sum for given range a and b is
def sum(f: Int => Int, a: Int, b: Int) = {
...
}
sum(x=>x, 3, 6) // outcome is 18 (3+4+5+6)
is
def sum(f: Int => Int): (Int, Int) => Int = {
def sumF(a: Int, b: Int): Int =
if (a > b) 0
else f(a) + sumF(a + 1, b)
sumF
}
sum(x=>x)(3, 6) // outcome is 18 (3+4+5+6)
But I don't understand why colon(:)
exists between (f: Int => Int) and (Int, Int)
in def sum(f: Int => Int): (Int, Int) => Int = {
instead of
def sum(f: Int => Int)(Int, Int) => Int = {
Your sum example is not curried. If you wanted it curried you'd do something like:
def sum(f: Int => Int)(a: Int, b: Int): Int =
if (a > b) 0
else f(a) + sum(f)(a + 1, b)
sum(x=>x)(3, 6) // res0: Int = 18
Your code defines a method that takes a single argument, def sum(f: Int => Int). That argument is a function that takes and Int and returns an Int. So no currying involved.
This sum method returns a function, : (Int, Int) => Int. This returned function takes 2 Ints and returns and Int. Invoking this sum method looks like currying, but it's not.
sum(x=>x)(3, 6)
Instead you are invoking sum() with a single argument (x=>x) and then invoking the returned function with two arguments (3,6).
(Int, Int) => Int between : and = specify the function's return type, i.e, it says sum will return another method of signature (Int, Int) => Int which takes two Int and returns another Int, and this is the signature of your inner sumF function:
You can rewrite this to the currying syntax as follows:
def sum(f: Int => Int)(a: Int, b: Int): Int = {
def sumF(a: Int, b: Int): Int =
if (a > b) 0
else f(a) + sumF(a + 1, b)
sumF(a, b)
}
This will more or less do the same thing as the method defined in OP:
sum(x => x)(3, 6)
// res11: Int = 18
But these two definitions are not exactly the same, for instance, for the currying syntax defined here, if you want to generate a new method from it, you have to specify the variable type, like:
val mysum: (Int, Int)=> Int = sum(x => x)
But for the one in OP, it can be simply val mysum = sum(x => x) as the return type of sum as already been specified.
I have a function named sum that can take a single parameter anonymous function as a parameter, and two Integers.
def sum (f: Int => Int , a: Int, b: Int): Int =
{
if(a > b) 0 else f(a) + sum(f, a + 1, b)
}
sum((x: Int) => x , 2, 10)
How could I modify the function definition so that it can take a multiple parameter function, so I could call it like this:
sum((y: Int, i: Int) => y + i => x , 2, 10)
I know the function I have supplied would be pretty useless when passed a multiple parameter function.. but I am just looking for how it can be done..
Thanks
As simple as this:
def sum (f: (Int, Int) => Int , a: Int, b: Int): Int = ???
Or a curried version:
def sum (f: Int => Int => Int , a: Int, b: Int): Int = ???
Although you can curry any function just calling f.curried
I have this code:
for( i <- 0 to 8){
((numbers(i) - i)/3).abs + ((numbers(i) - i)%3).abs
}
and I would like to do, as the title says, something like this
for( i <- 0 to 8){
by3(numbers(i), i, /) + by3(numbers(i), i, %)
}
def by3(a: Int, b: Int, op: Int => Int) = ((a - b) op 3).abs
and probably also use a partially applied function for it.. but by now this would be possible to achieve? How?
First, you need to correctly define op as a function (specifically, a Function2)
def operate(a: Int, b: Int, op: (Int, Int) => Int ) : Int = (op ((a - b), 3)).abs
Operators in Scala are actually methods: + is a method of Int(and Long, Double, ...) in Scala OO foundation. Then, to pass a operator (method) as a function, you can lift it using the underscore notation:
operate(5, 3, _ + _)
To get rid of the underscores you need to define the functions as values.
val / = (a:Int, b: Int) => a / b
val % = (a:Int, b: Int) => a % b
def by3(a: Int, b: Int, fn: (Int, Int) => Int): Int = fn(a - b, 3).abs
(0 to 8).foreach(i => by3(numbers(i), i, /) + by3(numbers(i), i, %))
Edited/Update:
Being concise, the shortest way to do it is:
def doStuff(a: Int, b: Int, op: (Int, Int) => Int) = {op(a - b, 3).abs}
doStuff(4,1,_%_)
So you can doStuff(numbers(i), i, _ / _) + doStuff(numbers(i), i, _ % _)