Coding Log

스위프트 찔러보기 - (3) 함수와 클로저(Functions and Closures)

본 블로그에 올라가는 포스팅은 애플의 공식 사이트를 대강 번역해서 작성하는 것이기 때문에 아래 링크에서 찔러보기 포스팅에서 쓸 예제 코드를 받을 수 있다.

참고 애플의 Playground를 이용한 Swift Example 다운로드 링크

함수의 선언과 호출

Swift에서는 func 키워드를 사용해 함수를 선언한다.

함수를 호출 할때는 함수의 이름 뒤에 오는 괄호 안에 파라미터를 넣어서 호출한다.

->를 사용하여 파라미터의 목록과 리턴 타입을 분리한다.

func greet(person: String, day: String) -> String { // 파라미터와 리턴 타입의 분리 
    return "Hello \(person), today is \(day)."
}
greet(person: "Bob", day: "Tuesday")

기본적으로 함수는 파라미터의 이름을 파라미터명으로 쓴다.

위의 예제코드를 다시 보자

func greet(person: String, day: String)

첫 번째 파라미터의 이름은 person이다.
두 번째 파라미터의 이름은 day이다.

따라서 호출 할때도 파라미터명을 명시한다.

greet(person: "Bob", day: "Tuesday")

위와 같이 말이다.

만약에 사용자가 직접 파라미터명에 레이블을 붙이고 싶다면 아래와 같이 파라미터 앞에 명시한다.

func greet(_ person: String, on day: String) -> String {
    return "Hello \(person), today is \(day)."
}
greet("John", on: "Wednesday")

위의 코드에서 person 앞에는 _이 있고 day 앞에는 on이 있다.

_는 파라미터명을 쓰지 않겠다는 뜻이고, on은 day 대신 파라미터명으로 쓰겠다는 뜻이다.

튜플을 이용한 다중 리턴 타입

튜플(tuple)을 사용하면 리턴 값을 여러개 반환할 수 있다.

아래의 예제를 보자.

func calculateStatistics(scores: [Int]) -> (min: Int, max: Int, sum: Int) {
    var min = scores[0]
    var max = scores[0]
    var sum = 0
 
    for score in scores {
        if score > max {
            max = score
        } else if score < min {
            min = score
        }
        sum += score
    }
 
    return (min, max, sum)
}
let statistics = calculateStatistics(scores: [5, 3, 100, 3, 9])
print(statistics.sum) // 120 출력 
print(statistics.0) // 3 출력 
print(statistics.1) // 100 출력 
print(statistics.2) // 120 출력 

예제를 보면 (min: Int, max: Int, sum: Int)의 튜플 형식을 이용해 여러 개의 리턴값을 반환하는 것을 알 수 있다.

print(statistics.sum) // 120 출력 
print(statistics.0) // 3 출력 
print(statistics.1) // 100 출력 
print(statistics.2) // 120 출력 

출력 부분을 자세히 보면 리턴되는 튜플에 요소의 이름이나 인덱스로도 접근할 수 있음을 알 수 있다.

배열 파라미터

함수의 파라미터는 배열로도 쓸 수 있다.

배열을 이용하면 정해지지않은 여러 개의 값을 함수의 파라미터로 넘길 수 있다.

func sumOf(numbers: Int...) -> Int {
    var sum = 0
    for number in numbers {
        sum += number
    }
    return sum
}
sumOf() // 0 
sumOf(numbers: 42, 597, 12) // 651 

함수의 중첩 사용

함수는 한 번에 여러개를 중첩해서 사용할 수 있다.

func A() {
  // A Body 
  func B() {
    // B Body 
  }
}

위의 형태에서 중첩되어 있는 함수 B는 B를 둘러싼 외부 함수 A의 변수에 접근할 수 있다.

함수의 중첩을 이용하면 길고 복잡한 함수를 정리하여 구성할 수 있다.

func returnFifteen() -> Int {
    var y = 10
    func add() {
        y += 5 // 10 + 5 
    }
    add()
    return y // 15 
}
returnFifteen() // 15 

함수를 반환하기

함수는 최상위 타입으로써, 특정 함수 A가 또 다른 함수 B를 리턴타입으로 쓸 수 있음을 의미 한다.

func makeIncrementer() -> ((Int) -> Int) {
    func addOne(number: Int) -> Int {
        return 1 + number
    }
    return addOne
}
 
var increment = makeIncrementer()
increment(7)

위의 예제에서 addOne 함수만 뜯어서 보자.

func addOne(number: Int) -> Int {
    return 1 + number
}
return addOne

addOne 함수는 정수 하나를 파라미터로 받아 1을 더한 후 반환하는 함수이다.

다시 전체를 보자.

func makeIncrementer() -> ((Int) -> Int) {
    func addOne(number: Int) -> Int {
        return 1 + number
    }
    return addOne
}

addOne함수를 반환하는 makeIncrementer함수의 반환타입을 잘 보자.

addOne함수의 형태가 Int를 받아 Int를 리턴하는 즉, (Int) -> Int 형태기 때문에 makeIncrementer함수의 반환 타입은 (Int) -> Int이 되는 것이다.

var increment = makeIncrementer() // increment에 함수를 저장한다. 
increment(7) // increment는 함수기때문에 파라미터를 사용할 수 있다. 

함수 파라미터

함수가 함수를 반환할 수 있듯이 함수가 함수를 파라미터로 받을 수도 있다.

func hasAnyMatches(list: [Int], condition: (Int) -> Bool) -> Bool {
    for item in list {
        if condition(item) {
            return true
        }
    }
    return false
}
func lessThanTen(number: Int) -> Bool {
    return number < 10
}
var numbers = [20, 19, 7, 12]
hasAnyMatches(list: numbers, condition: lessThanTen)

다시 천천히 하나씩 보기 위해서 lessThanTen함수부터 보자

func lessThanTen(number: Int) -> Bool {
    return number < 10
}

lessThanTen함수는 정수를 입력받아 10보다 작으면 true, 10 이상이면 false를 반환하는 함수이다.

함수 호출부를 잘 보면

hasAnyMatches(list: numbers, condition: lessThanTen)

파라미터명 condition에 lessThanTen함수를 파라미터로 넘기는 것을 볼 수 있다.

func hasAnyMatches(list: [Int], condition: (Int) -> Bool) -> Bool {
    for item in list {
        if condition(item) { // lessThanTen(item) 
            return true
        }
    }
    return false
}

위의 condition이 lessThanTen함수와 동일한 역할을 함을 알 수 있다.

함수와 클로저

함수는 클로저(Closure)의 특별한 한 형태이다.

클로저란 함수와 동일하게 파라미터를 받을 수 있고 반환값이 존재하는 중괄호 { }로 둘러싸인 코드의 영역을 말한다.

즉 이름이 있는 클로저가 함수인 것이다.

클로저는 클로저가 작성된 범위내에서 변수와 함수에 접근할 수 있으며, in을 사용해서 파라미터와 리턴 타입영역과 클로저의 코드 영역을 분리한다.

numbers.map({
    (number: Int) -> Int in
    let result = 3 * number
    return result
})

클로저만 이해할 수 있도록 코드의 들여쓰기를 바꿔보자.

numbers.map(
  {
    (number: Int) -> Int in
      let result = 3 * number
      return result
  }
)

위에 있는 클로저는 파라미터명 number에 정수를 받아 정수를 리턴하며, 코드 영역은

let result = 3 * number
return result

위의 부분이다.

조금 어렵지만 클로저는 간결하고 유연하게 사용하는 것에 의미가 있다.

위의 복잡한 코드 대신 간결하게 클로저를 작성하는 방법을 몇 가지 알아보자.

클로저의 형식을 이미 알고 있는 경우엔 Swift의 타입추론을 통해 파라미터와 리턴 타입을 추론한다.

따라서 파라미터의 유형과 반환 값의 유형을 하나 혹은 둘 다 생략할 수 있다.

let mappedNumbers = numbers.map({ number in 3 * number })
print(mappedNumbers)

마찬가지로 Swift의 타입 추론을 이용하여 파라미터명 대신 숫자를 이용할 수 있다.

위와 같은 방법은 클로저를 짧고 간결하게 만드는 데 특히 유용하다.

var numbers = [20, 19, 7, 12]
 
let sortedNumbers = numbers.sorted { $0 > $1 }
print(sortedNumbers) // [20, 19, 12, 7] 출력 

 


DISQUS 로드 중…
댓글 로드 중…

트랙백을 확인할 수 있습니다

URL을 배껴둬서 트랙백을 보낼 수 있습니다