The following two higher-order functions yield the same result, but I'm having trouble understanding the difference between the two. In fact, I don't understand 1). How can a function have two sets of ()?
1)
def sum(f: Int => Int) (a: Int, b: Int) = {
def loop(a: Int, acc: Int) : Int =
if (a > b) acc
else loop (a + 1, f(a) + acc)
loop (a, 0)
}
2)
def sum(f: Int => Int, a: Int, b: Int) = {
def loop(a: Int, acc: Int) : Int =
if (a > b) acc
else loop (a + 1, f(a) + acc)
loop (a, 0)
}
The first function is curried. Meaning you can partially apply it more easily. If you only use the first parameter list the function returns another function with the signature (Int, Int) => Int.
This is really useful if you need to pass a function with a specific function signature to higher order functions like map or reduce.
In other functional programming languages like Haskell, all functions are curried by default.
I wrote a whole blog post on this topic in case you're interested. You can read it right here.
Related
I want to get the sum of a seq of Integers in Scala.
In my mind I want to fold a plus over the integers like this:
val seqOfIntegers:Seq[Int] = Seq(1, 2, 3, 4, 5)
val sumOfIntegers = seqOfIntegers.reduce(+)
This isn't valid.
Instead I have to do:
val sumOfIntegers = seqOfIntegers.reduce(plus)
...
def plus(a:Integer, b:Integer): Integer = { a + b}
(I'm sure you could sugar that up - but my point is that the original plus symbol doesn't work as a function, and the errors messages don't make it clear why.)
My question is: Why can't I reduce(+) on a seq of integers in Scala?
That's because + is not a function (that is, an object of type Function2[Int, Int, Int], which is the same as (Int, Int) => Int). Instead, it's a method on Int, which takes another Int and returns an Int. That is, what you want to pass to reduce is
actually (a: Int, b: Int) => a.+(b), which can be sugared into (a: Int, b: Int) => a + b and then _ + _.
That is,
seq.reduce(_ + _)
would work as expected.
Making .reduce(+) work
If you want to pass + as argument, you have to define a value + that extends (Int, Int) => Int. For example:
object + extends ((Int, Int) => Int) { def apply(a: Int, b: Int): Int = a + b }
Seq(1,2,3,4,5).reduce(+)
// res0: Int = 15
or, additionally relying on eta-expansion:
def +(a: Int, b: Int) = a + b
Seq(1,2,3,4,5).reduce(+)
So, you can invoke .reduce(+) on sequences, you just need either a value + of the right type (Int, Int) => Int (a value, not a method), or you need a binary method + in scope, which takes two integers, and can be eta-expanded into (Int, Int) => Int.
On error messages
The error messages seem quite unambiguous. If I don't define + myself, and enter
Seq(1, 2).reduce(+)
into the console, then I get
error: not found: value +
because there is no value + anywhere. I don't know how the error message could be any clearer.
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'm following a Coursera course for functional programming in Scala so that I can learn the language.
They introduced the concept of tail-recursive functions and defined them basically as a function that ends in a call to itself. But then they show this as the worked example:
def sum(f: Int => Int)(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)
}
If I annotate sum with #tailrec, I get an error because the IDE (IntelliJ) does not consider it to be tail recursive.
Is sum called tail-recursive here, or is the inner implementation "loop" the thing that is considered to be tail recursive in this case?
You're right. sum needs to call itself otherwise Scala will complain with:
#tailrec annotated method contains no recursive calls
If you tell compiler where exactly it is, by moving the #tailrec to where the tail recursion is:
def sum(f: Int => Int)(a: Int, b: Int): Int = {
#tailrec def loop(a: Int, acc: Int): Int = {
if (a > b) acc
else loop(a + 1, f(a) + acc)
}
loop(a, 0)
}
Then the compiler'll be satisfied it is tail-recursion optimised.
The inner loop method is tail-recursive, the sum method is not recursive at all. Therefore, you should annotate loop with #tailrec.
Moving loop to the outer scope may help visualize what's going on:
def sum(f: Int => Int)(a: Int, b: Int): Int = {
loop(a, 0)
}
def loop(a: Int, acc: Int): Int = {
if (a > b) acc
else loop(a + 1, f(a) + acc)
}
As you can see, sum is merely a public entry point for loop.
(Note that the code above won't compile because loop does not close over b or f anymore, but you get the point.)
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, _ % _)