KoreanFoodie's Study
Scala 5 - Currying 본문
스칼라 튜토리얼, scala에서 currying에 대해 알아보자
Currying
Why we use currying? Let's look at the code, simplified by currying.
def sum(f: Int=>Int, a: Int, b: Int): Int =
if (a <= b) f(a) + sum(f, a+1, b) else 0
def linear(n: Int) = n
def square(n: Int) = n * n
def cube(n: Int) = n * n * n
def sumLinear(a: Int, b: Int) = sum(linear, a, b)
def sumSquare(a: Int, b: Int) = sum(square, a, b)
def sumCubes(a: Int, b: Int) = sum(cube, a, b)
We want the following. How?
def sumLinear = sum(linear)
def sumSquare = sum(square)
def sumCubes = sum(cube)
- Solution :
def sum(f: Int => Int): (Int, Int) => Int = {
def sumF(a: Int, b: Int): Int =
if (a <= b) f(a) + sumF(a+1, b) else 0
sumF
}
def sumLinear = sum(linear)
def sumSquare = sum(square)
def sumCube = sum(cube)
sumSquare(3, 10) + sumCubes(5, 20)
// We don't need to define the wrapper funcitons
sum(suquare)(3, 10) + sum(cube)(5, 20)
We can also write as follows :
def sum(f: Int => Int): (Int, Int) => Int =
(a, b) => if (a <= b) f(a) + sum(f)(a+1, b) else 0
If compiler is not smart as we expected, above function should make anonymous function (a, b) => sth
everytime sum(f)
is called, so it could be slower than the function using sumF
. (Because anonymous function doesn't have its own name, however, once sumF
is defined, it is used without need to be declared again) But if the compiler optimizes this code(because sum(f)
is used recursively), it may not be executed slow.
Or more simply, and probably the best way :
def sum(f: Int => Int)(a: Int, b: Int): Int =
if (a <= b) f(a) + sum(f)(a+1, b) else 0
Currying and Uncurrying
A function of Type
(T1, T2, ... ,Tn) => T
can be turned into one of type
T1 => (T2 => (... Tn =>T)
This is called 'currying' named after Haskell Brooks Curry. The opposite direction is called 'uncurrying'.
Currying using Anonymous Functions
def foo(x: Int, y: Int, z: Int)(a: Int, b: Int) =
x + y + z + a + b
val f1 = (x: Int, z: Int, b: Int) => foo(x, 1, z)(2, b)
val f2 = foo(_: Int, 1, _: Int)(2, _: Int)
val f3 = (x: Int, z: Int) => (b: Int) => foo(x, 1, z)(2, b)
f1(1, 2, 3)
f2(1, 2, 3)
f3(1, 2)(3)
Excercise : Cutty the mapReduce function
def mapReduce(combine: (Int,Int)=>Int,inival: Int,
f: Int=>Int, a: Int, b: Int): Int = {
if (a <= b) combine(f(a),mapReduce(combine,inival,f,a+1,b))
else inival
}
def sum(f: Int=>Int, a: Int, b: Int): Int =
mapReduce((x,y)=>x+y,0,f,a,b)
def product(f: Int=>Int, a: Int, b: Int): Int =
mapReduce((x,y)=>x*y,1,f,a,b)
- Solution :
def mapReduce2(combine: (Int, Int) => Int, inival: Int)
(f: Int => Int) (a : Int, b: Int): Int ={
if (a <= b) combine(f(a), mapReduce2(combine, inival)(f)(a+1, b))
else inival
}
// need to make a closure since mapReduce is param. code.
def sum2 = mapReduce2((x, y) => x+y, 0) _
// val is better than def. Think about why.
// If you use 'def', you need to 'close' your code everytime is is called.
val product2 = mapReduce2((x, y) => x*y, 1) _
sum2(linear)(0,100)
product2(square)(1, 5)
You need to add '_'(underbar) after def sum2
so that it explicitly declares include methods invisible.
(Error message : missing argument list for method mapReduce2 in class. Unapplied methods are only converted to functions when a function type is expected. You can make this conversion explicit by writing mapReduce2 _
or mapReduce2(_,_)(_)(_,_)
instead of mapReduce2
.)
Exceptions
class factRangeException(val arg: Int) extends Exception
def fact(n: Int): Int =
if (n < 0) throw new factRangeException(n)
else if (n == 0) 1
else n * fact(n -1)
def foo(n : Int) = fact(n + 10)
try {
println(fact(3))
println(foo(-100))
} catch {
case e : factRangeException => {
println("fact range error: " + e.arg)
}
}
'Tutorials > Scala' 카테고리의 다른 글
Scala 7 - Datatypes2 (0) | 2019.04.23 |
---|---|
Scala 6 - Datatypes (0) | 2019.04.23 |
Scala 4 - Closures, revisted (0) | 2019.04.23 |
Scala 3 - Tail Recursion & Higher-Order Functions 1 (0) | 2019.04.23 |
Scala 2 - Blocks in Scala & Lazy val (0) | 2019.04.23 |