Closure


오늘은 고차함수를 배웠는데 고차함수를 이해하기 전에 클로저를 먼저 선행해야 겠다는 생각에 계속해서 클로저에 대해 공부하고 정리했지만 아직 머리에 남지 않아 야곰님의 강의를 듣고 다시 나의 식으로 정리하려 한다


클로저란? 그리고 클로저를 만드는 방법?

클로저는 일단 코드의 블럭이라고 표현 할 수 있다 그리고 클로저는 일급시민으로서

변수, 상수등으로 저장이 가능하고, 전달인자로 전달이 가능하다

함수는 이름이 있는 클로저 즉, 클로저의 일종이라고 보면 된다

클로저는 다음과 같이 정의된다

일단 먼저 중괄호로 묶어주고 그 안에 소괄호를 넣어주는데 소괄호 안에는 매개변수 목록을 적어준다

반환 타입이 있다면 소괄호 다음에 -> 를 해주고 반환 타입을 써 주면 된다

반환 타입 다음에는 in 키워드가 써지는데 그 in 키워드 다음에는 실행 코드를 적어주면 된다

즉 아래처럼 정의가 된다

{ (매개변수 목록) -> 반환타입 in
    실행코드
}

만약에 매개변수 목록이 없다면 매개변수 목록을 다 지워지고 빈 소괄호만 만들어주면 된다

그리고 만약에 반환타입이 없다면 반환타입이 들어가야 할 부분에 Void를 넣어주면 된다

즉, 매개변수가 없고 반환타입이 없는 클로저를 정의하려면 아래와 같이 정의하면 된다

{ () -> Void in
    실행코드
}

실제로 클로저를 만들어 보자

만약에 아래와 같은 함수가 있다는데 이 함수를 클로저 형태로 바꿀 수 있다

func sumFunction(a: Int, b: Int) -> Int{
    return a + b
}

일단 먼저 중괄호를 열어 준다

{}

그리고 그 안에 소괄호를 열어 매개변수 목록을 넣어 주면 된다

{(a: Int, b: Int)}

매개변수 목록을 작성했으니 위의 함수를 참고하여 보면 반환타입이 Int형이니 -> 다음에 Int 형을 반환 받는 다는 것을 명시해 주면 된다

{(a: Int, b: Int) -> Int}

이제 반환타입이 Int 형이라는 것을 알았으니 이 클로저를 호출하면 실행할 구문을 작성해 주어야 하는데,

그 실행 구문은 반환타입 다음에 in 이라는 키워드를 사용하여 그 in 키워드 뒤에 작성해 주면 된다

그러면 아래와 같이 된다

{ (a: Int, b: Int) -> Int in
    a + b
}

클로저의 사용

위의 sumFunction 함수를 이용하여 다시 보자

아래와 같은 함수를 만들었다고 가정하자

func sumFunction(a: Int, b: Int) -> Int{
    return a + b
}

예를들어 어느 변수에 위의 함수를 할당하여

그 함수를 사용하려고 한다면 다음과 같이 코드가 작성 될 것이다

var sumResult: Int = sumFunction(a: 10, b: 10)

print(sumResult) // 20

그렇다면 클로저를 사용하여 할당 할 수 있지 않을까??

왜냐하면 함수는 클로저의 일종 즉, 이름이 있는 클로저이므로 함수가 가능한 부분이면

클로저도 분명히 가능하기 때문이다

그러면 클로저를 사용해보자

일단 먼저 클로저를 할당해줄 변수를 만들고 그 변수의 타입을 지정해 주는데

그 타입은 인자값으로 2개를 받는데 그 2개가 모두 int 타입이고

반환값으로 int 값을 반환 받는다

그것을 코드로 구현하면 아래와 같이 된다

var sumResult: (Int, Int) -> Int

클로저를 받을 변수를 만들고 그 변수의 타입까지 클로저 형태로 타입을 지정해줬다

그렇다면 이제 클로저를 직접 할당해주면 되는데 그 코드는 아래와 같다

var sumResult: (Int, Int) -> Int = {(a: Int, b: Int) -> Int in a + b}

함수의 전달인자로 활용되는 클로저

함수의 전달인자로 클로저는 활용이 되는데

더하기, 빼기, 나누기를 클로저 형태로 만들어 상수에 할당하는 과정부터 한번 보자

먼저 상수를 만들고 거기에 클로저를 할당해주면 아래와 같다

let add: (Int, Int) -> Int
add = { (a: Int, b: Int) -> Int in 
    a + b
}

let substract: (Int, Int) -> Int
substract = { (a: Int, b: Int) -> Int in
    a - b
}

let divide: (Int, Int) -> Int
divide = { (a: Int, b: Int) -> Int in
    a / b
}

이렇게 클로저를 각각의 상수에 맞게 넣어주었다 그렇다면 이 각각의 상수를 함수의 전달인자로

활용 할 수 있는데 아래의 코드를 보면서 알아보자

먼저 함수를 정의 해보자

이 함수에는 3개의 인자값을 받는데 첫번째 인자값과 두번째 인자값 모두 Int 타입이고 메소드라는 인자값이 있는데 이 메소드는 (Int,Int) -> Int 타입의 클로저를 받아온다 그리고 리턴 타입으로 Int 를 받는다

여기서 제일 중요한 함수의 전달인자로 활용되는 클로저가 바로 저 메소드라는 인자값이다

func calculate(a: Int, b: Int, method: (Int, Int) -> Int) -> Int {
    return method(a,b)
}

위에서 말은 안햇지만 또 중요한 점이 잇는데 위의 코드를 보면 return이 보일것이다

이 리턴은 method를 호출해준다 이 점이 굉장히 중요하다.

다시말해, 함수 안에서 전달받은 클로저를 호출해주는 것이다

이 함수를 한번 실행해보면 함수의 전달인자로 활용되는 클로저의 구문에 대해 더 이해가 잘될것이다

먼저 Int 타입의 변수를 하나 만든다

var calculated: Int

이 변수에 calculate를 호출해 각각의 인자값에 알맞는 값을 넣어주면 함수가 실행된다

calculated = calculate(a: 10, b: 20, method: add) // 30
calculated = calculate(a: 10, b: 20, method: substract) // -10
calculated = calculate(a: 20, b: 10, method: add) // 2

여기서 중요한점은 각각의 함수 인자값 중 method에 들어간 인자값을 보면

위에서 먼저 만들어 놓은 클로저를 인자값으로 받아 함수의 리턴값을 method가 받아오는 인자값에

따라서 각기 다른 결과값을 가져온다

return method(a,b)

예를 들어 add이면 먼저 인자값으로 add를 전달받고 그 전달 받은 인자값 add가 return method(a,b)에서 a + b 가 되어 값이 리턴 되는 것이다

위의 예시는 미리 만들어 놓은 클로저를 인자값으로 받았는데 아래와 같이 매개변수의 전달인자로 클로저 코드블럭을 넣어 줘도 된다

아래 코드는 곱하기를 구현한 것이다

calculated = calculate(a: 50, b: 10, method: { (left: Int, right: Int) -> Int in left * right } -> Int {
    return method(a,b)
}

print(calculated) // 500