Scala는 함수형 프로그래밍 언어이다. 그래서 Scala에서는 함수가 매우 중요하다. Scala에서 함수는 이름이 있는 재활용한 가능한 "expression"들이다. 파라미터를 받을수도 있고 값을 반환할수도 있다. 함수형 프로그래밍 언어 방법론에 있어서 "pure" 함수는 다음의 특성을 가진다.
1) 하나이상의 입력 파라미터를 가진다.
2) 오직 입력 파라미터들만을 가지고 계산을 수행한다.
3) 값을 반환한다
4) 동일한 입력에 대해서 동일한 값을 반환한다.
5) 함수 밖의 데이터를 사용하거나 영향을 주지 않는다.
6) 함수 밖의 데이터에 의해서 영향받지 않는다.
"pure" 함수는 stateless 하고 외부 데이터에 영향을 받지 않기 때문에 안정적이다. 물론 Pure함수만으로 응용 프로그램을 만들수는 없다. 하지만 pure 함수를 많이 사용하려고 노력해야 한다.
다음은 Scala의 함수를 정의하는 방법이다.
def <identifier>(<identifier:<type>[, ...]): <type> = <expression> |
def <identifier> = <expression> 으로 입력이 없는 함수를 정의할 수 있다. 다음은 함수 정의 실행 예이다.
def greeting_1="hello" def greeting_2:String="hello" def greeting_3()="hello" def greeting_4():String="hello" def greeting_5(name:String):String="Hello "+name |
greeting_3() 또는 greeting_3 로 호출이 가능하다. 하지만 greeting_2()로는 호출할수는 없다.
다음처럼 Expression Block과 같이 함수 호출을 할 수 있다.
def pow(x:Int) : Int = x*x pow {val inc=1;inc+1} |
이 값은 4가 나온다.
(Recursive Function)
다음과 같이 recursive 하게 호출할 수 있다.
def factorial(x:Int):Long = { if(x>1) x*factorial(x-1) else 1 } |
factorial(10)을 호출하게 되면 Long = 3628800 이 리턴한다.
recursive 함수는 stack overflow 가 발생할 수 있다. scala에서는 recursive 함수중 tail-recursion으로 optimize 할수 있는 방법을 제공한다. 이는 recursive call이 추가적인 stack space를 사용하지 않는 것이다. tail-recursion은 추가적인 stack 을 할당하지 않고 현재의 stack 공간을 사용한다. 함수중 마지막 문장이 recursive 호출일 경우에면 tail-recursion으로 최적화될 수 있다. @annotation.tailrec 로 compiler에 지시할수 있다. 예를 들어 앞의 factorial 함수는 다음과 같이 optimize 될 수 있다.
@annotation.tailrec def factorial(x:Int,sum:Long=1):Long={ if(x<2) sum else factorial(x-1,sum*x) } |
주의해야할 점은 마지막 문장이 recursive 호출이어야 한다. 예를 들어 다음과 같이 정의할 경우 compile 에러가 발생한다.
@annotation.tailrec
def factorial(x:Int):Long={
if(x<1) 1
else x*factorial(x-1)
}
이렇게 하게 되면 맨 마지막 문장이 recursive call이 아니고 x*factorial(x-1)로 곱하기 연산이 되기 때문에 에러가 발생한다.
(Nested Function)
함수 내에 함수를 정의할 수 있다. 다음은 그 예제이다
def add(x:Int,y:Int):Long={ def add(x:Int,y:Int):Long={ def add(x:Int,y:Int):Int=x+y add(x,y)+add(x,y) add(x,y)+add(x,y) } |
add(10,10)을 호출하면 Long=80 이 출력된다. 같은 add라는 함수가 내부에 다시 정의되었지만 이름 충돌은 발생하지 않는다. 함수 내에서는 지역적으로 함수 이름이 적용된다.
(Named & Default Parameter)
함수에 파라미터를 전달할 때 이름을 지정하여 전달 가능하다. 다음은 그 Syntax이다
<function name> ( <parameter> = <value> ) |
다음은 그 활용 예이다.
scala> def greeting(greet:String,name:String)=greet+"! "+name greeting: (greet: String, name: String)String scala> greeting("hello","world") res3: String = hello! world scala> greeting(name="world",greet="hi") res4: String = hi! world |
함수를 정의할때 파라미터에 대한 기본 값을 지정할수 있다. 이를 이용하여 파라미터의 개수를 다르게 할수 있는 함수 오버로딩이 가능하다. 다음은 기본 값을 지정하는 함수 정의 Syntax이다.
def <identifier> (<identifier>:<type>=<value>):type = expression |
다음은 호출 사례이다.
scala> def greeting(name:String,msg:String="Hello "):String=msg+name greeting: (name: String, msg: String)String scala> greeting("World") res0: String = Hello World scala> greeting("World","Hi ") res1: String = Hi World |
(Vararg & Group Parameter)
가변적인 함수 파라미터를 정의할수 있다. 하나 이상의 파라미터를 받는 함수는 *를 붙여서 정의가능하다. Scala는 파라미터를 Grouping할 수 있다. 다음은 호출 사례이다.
scala> def sum(x:Int*):Int={ | var ret=0 | for(i<-x) ret+=i | ret | } sum: (x: Int*)Int scala> sum(1,2,3) res0: Int = 6 |
만약 * 파라미터 다음에 다른 파라미터를 추가하게 되면 (예를 들어: def sum_2(x:Int*,flag:Boolean):Int={) *-parameter must come last 에러가 발생한다. 다음과 같이 grouping 하여 파라미터를 다르게 전달할 수 있다.
scala> def sum_2(x:Int*)(flag:Boolean):Int={ | var ret=0 | for(i<-x) ret+=i | if(flag==true) println("Sum:"+ret) | ret | } sum_2: (x: Int*)(flag: Boolean)Int scala> sum_2(1,2,3)(true) Sum:6 res1: Int = 6 |
(Type Parameter)
파라미터의 Type은 함수 호출시에 지정하게 할 수 있다. 다음은 그 Syntax이다
def <function-name>[type-name](<parameter-name>:<type-name>):<type-name>... |
다음은 호출 예이다.
scala> def sum[A](x:A*):Int={ res3: Int = 60 |
class에 속한 함수는 method라고 한다. method는 infix dot notation 또는 operator notation 으로 호출한다. 다음은 infix dot notation 호출 Syntax이다.
<class instance>.<method>[(<parameters>)] |
다음은 operatior notation으로 호출하는 Syntax이다.
<object> <method> < parameter> |
다음은 그 호출 예이다.
scala> val test="Hello World" test: String = Hello World scala> test.split(" ") res9: Array[String] = Array(Hello, World) scala> test split " " res10: Array[String] = Array(Hello, World) |
함수의 주석은 /** (블록 주석) **/ 또는 // (라인 주석)으로 처리할 수 있다.