Swift의 클로저는 독립된 코드 블록으로, 매개변수를 전달해 계산을 실행하고 값을 반환할 수 있어요. 클로저는 또한 주변 환경에서 값을 캡처해서 계산에 사용하기도 해요. 스스로 완결되어 있기 때문에, 다른 값들처럼 프로그램 안에서 여기저기 전달되거나 상수와 변수에 할당될 수 있어요.
클로저는 Swift의 함수와 꽤 비슷하게 들릴 수 있어요. 실제로 비슷하고, 그럴 만한 이유도 있어요. Swift의 함수는 이름이 있어야 하고 조금 다른 문법으로 정의되는 특별한 클로저예요.
Swift의 함수도 엄밀히 말하면 클로저지만, 사람들이 Swift에서 "클로저"라고 할 때는 보통 클로저 표현식을 가리켜요.
클로저 표현식은 매개변수 목록, 반환 타입, in 키워드, 그리고 클로저의 본문 순서로 쓰고, 전체를 중괄호 한 쌍으로 감싸요:
{ (a: Double, b: Double) -> Double in
return (a + b) / 2.0
}
이 코드는 (Double, Double) -> Double 타입의 클로저 표현식을 정의해요. 그리고 이 타입의 함수와 비슷한 방식으로, 알맞은 타입의 매개변수에 적용해서 실행할 수 있어요:
{ (a: Double, b: Double) -> Double in
return (a + b) / 2.0
}(10.0, 15.0)
// returns 12.5
클로저를 실행할 때마다 전체 클로저 표현식을 매번 써 주는 건 그리 편리하지 않아요. 그래서 클로저는 Swift의 다른 값들처럼 이름에 할당할 수 있어요. 그러면 이름에 매개변수를 적용하는 방식으로 호출할 수 있어요:
let mean = { (a: Double, b: Double) -> Double in
(a + b) / 2.0
}
mean(13.0, 100.0)
// returns 56.5
위에서 봤듯이, 클로저도 일반 함수처럼 본문이 하나의 표현식이면 return 키워드를 생략할 수 있어요.
클로저는 주로 고차 함수와 함께 사용되는데, 매개변수로 전달되거나 결과로 반환되기도 해요:
[11, 75, 3, 99, 53].contains(where: { (x: Int) -> Bool in x > 100 })
// returns false
["apple", "ball", "carrot"].sorted(by: { (s1: String, s2: String) -> Bool in s1.count < s2.count })
// returns ["ball", "apple", "carrot"]
func makeAdder(base: Int) -> (Int) -> Int {
{ (x: Int) -> Int in base + x }
}
또 만났네요, Double-Null0111 요원.
UMBRA의 세력이 다시 꿈틀거리고 있고, 그들의 최신 계략을 막아야 해요. 정보부에 따르면, 그 미니언들이 주지사의 무도회에서 악취 폭탄을 터뜨릴 계획이래요.
미니언들은 워낙 덜렁대서 실험실이나 사무실에서 실수로 악취 폭탄을 무장시키곤 해요. 게다가 악취 폭탄을 해체하는 방법을 자꾸 잊어버리기 때문에, 폭탄의 ID 번호로부터 전선을 잘라야 하는 순서를 나열하는 해체 지침을 생성하는 시스템을 만들어 놨어요.
이제 여러분이 할 일은 무도회에 있는 악취 폭탄이 터지기 전에 해체할 수 있게 하는 코드를 작성하는 거예요.
악취 폭탄에는 각각 색이 다른 전선 세 개가 있어요.
(String, String, String) 튜플을 받아서 처음 두 원소의 순서를 뒤집은 튜플을 반환하는 클로저를 작성해요.
이 클로저를 flip이라는 이름에 할당해요.
주의할 점은 타입이 ChangeClosure(또는 sendable 타입)여야 한다는 거예요. 타입 정의는 스텁 파일에 나와 있어요.
그렇지 않으면 코드가 컴파일되지 않아요.
flip(("red", "yellow", "blue"))
// Returns ("yellow", "red", "blue")
(String, String, String) 튜플을 받아서 전선의 순서를 왼쪽으로 회전시킨 튜플을 반환하는 클로저를 작성해요.
이 클로저를 rotate라는 이름에 할당해요.
주의할 점은 타입이 ChangeClosure(또는 sendable 타입)여야 한다는 거예요. 타입 정의는 스텁 파일에 나와 있어요.
rotate(("red", "yellow", "blue"))
// Returns ("yellow", "blue", "red")
악취 폭탄의 전선을 자를 순서를 알아내려면, 악취 폭탄의 ID 번호를 바탕으로 적절한 셔플 함수를 생성하는 함수를 구현해야 해요.
다음 함수를 구현해요.
makeShuffle(
flipper: @escaping ((String, String, String)) -> (String, String, String),
rotator: @escaping ((String, String, String)) -> (String, String, String)
) -> ([UInt8], (String, String, String)) -> (String, String, String)
이 함수는 전선 두 개를 뒤집는 클로저와 전선 세 개를 회전시키는 클로저를 입력으로 받아서 클로저를 반환해요. 반환된 클로저는 악취 폭탄의 ID 번호와 전선 세 개의 순서를 받아서, 전선을 잘라야 하는 순서를 계산해요. ID 번호는 8개의 비트로 이루어져 있고, 각 비트가 전선을 뒤집을지 회전시킬지를 결정해요.
ID 번호의 각 비트에 대해, 가장 오른쪽 비트부터 시작해서, 비트가 0이면 flipper 클로저를 전선 튜플에 적용하고, 1이면 rotator 클로저를 적용해서 전선의 새로운 상태를 만들어요.
ID의 여덟 개 비트 모두에 알맞은 클로저를 적용하고 나면, 전선의 최종 상태가 바로 잘라야 하는 순서예요.
let shuffler = makeShuffle(flipper: flip, rotator: rotate)
// Returns ([UInt8], (String, String, String)) -> (String, String, String)
shuffler([1, 0, 0, 1, 1, 0, 1, 1], ("red", "yellow", "blue"))
// Returns ("red", "blue", "yellow")
Exercism에 가입하고 Swift 트랙을 개념 35개연습 문제 116개, 그리고 실제 사람의 멘토링과 함께 배우고 익혀 보세요. 모두 무료예요.