[Memory Reading] Programming in Scala 8. 함수와 클로저

8. 함수와 클로저

8.1 메소드

객체의 맴버인 함수

8.2. 지역 함수

잘 정의한 작업을 수행하는 다수의 작은 함수로 프로그램을 나눠야 한다. 더 어려운 일을 처리하기 위해 유연하게 조립할 수 있는 건축 블록을 제공

도우미 함수들이 네임스페이스를 오염시키는걸 방지하기 위해 Java에서는 private 메소드를 사용

스칼라에서는 함수안에 함수를 정의.

함수안에 정의한 지역 함수도 그 정의를 감싸고 있는 블록 내에서만 접근할 수 있다.

지역 함수는 바깥쪽의 블록의 파라미터에 접근 할 수 있다.

def processFile(filename: String, width: Int) {
def processLine(line: String) {
while (line.length > width)
println(filename + ": " + line)
}
}

8.3. 1급 계층 함수

함수를 정의하고 호출할 뿐만 아니라, 이름 없이 리터럴로 표기해 값처럼 주고받을 수 있다.

함수 리터럴은 클래스로 컴파일하는데, 해당 클래스를 실행 시점에 인스턴스화하면 함수값이된다.

함수 리터럴은 소스코드에 존재하고 함수 값은 실행 시점에 객체로 존재한다.

함수 값은 객체이기 때문에 변수에 저장가능하다

// 함수 리터럴
var increase = (x: Int) => x + 1
var increase = (x: Int) => {
println("x : " + x)
x+1
}
increase(10)

함수 리터럴과 함수값을 사용

val someNumbers = List(-11,-10–5)
someNumbers.foreach( (x: Int) => println(x) )
someNumbers.filter( x => x > 0 )

8.4. 간단한 형태의 함수 리터럴

타깃 타이핑 : 위 예에서 someNumbers가 정수의 리스트이기 때문에 filter를 사용할때의 변수 x가 Int임을 추론할 수 있음

8.5. 위치 표시자 문법

함수 리터럴을 좀 더 간결하게 만들기 위해 밑줄을 하나 이상의 파라미터에 대한 위치 표시자로 사용할 수 있다. (단, 함수 리터럴에서 각 인자는 한번씩만 나타내야 한다)

// 같은 함수 리터럴
someNumbers.filter( _ > 0 )
someNumbers.filter( x => x > 0 )

8.6 부분 적용한 함수

전체 파라미터 목록을 밑줄로 바꿀 수 있다

someNumbers.foreach( x => println (x) )
someNumbers.foreach(println _)

부분 적용 함수는 함수에 필요한 인자를 전부 적용하지 않은 표현식을 말한다

def sum(a: Int, b: Int, c: Int) = a + b + c
// 1번
val a = sum _
a (1,2,3)
// 2번
a.apply(1,2,3)
// 3번
val b = sum (1, _: Int, 3)
b(2)

3가지 모두 결과는 6이다.

sum _는 인자를 전혀 넘기지 않고 모두 빠진 인자로 처리한다.

sum_는 사실 apply 함수를 호출하는 것이다.

중간인자가 빠진 b를 만들면 컴파일러는 인자를 하나만 받는 apply 메소드가 들어있는 새로운 함수 클래스를 만든다

someNumbers.foreach( println _)
someNumbers.foreach( println )

_ 도 생략할 수 있는데 foreach 처럼 함수가 필요한 시점만 가능하다

8.7 클로저

(x: Int) => x + more

자유 변수 (more) : 함수 리터럴에서 의미를 부여하지 않은 변수
바운드 변수 (x) : 함수의 문맥에서만 의미가 있는 변수

클로저 : 주어진 함수 리터럴로부터 실행 시점에 만들어낸 객체인 함수 값
클로저라는 이름은 함수 리터럴의 본문에 있는 모든 자유 변수에 대한 바인딩을 캡춰해서 자유 변수가 없게 닫는(closing) 행위에서 따온 말이다.

닫힌 코드 조각 : 자유변수가 없는 함수 리터럴
열린 코드 조각 : 자유변수가 있는 함수 리터럴

(x: Int) => x + more를 가지고 실행 시점에 만들어내는 함수 값에는 포획한 more 변수에 대한 참조가 들어 있기 때문에, 클로저라 부른다.

val someNumbers = List( -2, 0, 1 )
var sum = 0
someNumbers.foreach( sum += _ )
res20: Int = -1

클로저 안에서 포획한 변수를 변경하면 클로저 밖에서도 변경되어 있다.

def makeIncreaser(more: Int) = (x: Int) => x + more

이 함수를 호출할 때마다 새로운 클로저가 생긴다.

8.8 특별한 형태의 함수 호출

// 반복 파라미터
def echo (args : String* ) =
for (arg <- args) println(arg)
echo ("Hello", "World")
// 배열 반복 인자 전달
echo (arr: _*)
// 이름 붙인 인자
// 일반적인 함수 호출에서는 파라미터 순서가 맞아야 하지만 파라미터에 이름을 붙여주면 순서가 달라도 된다.
speed(distance = 100, time = 10)
// 디폴트 인자값
디폴트 값을 지정한 파라미터가 있다면, 함수 호출 시 해당 인자를 생략할 수 있다. 생략한 인자는 디폴트 값으로 채워진다.
def printTime(out: java.io.PrintStream = Console.out, divisor: Int = 1) =
out.println("time = " + System.currentTimeMillis() / divisor)

8.9 꼬리 재귀