[ Swift ] #4. 함수와 클로저

ma.caron_g·2022년 6월 29일
0

Swift

목록 보기
4/4
post-thumbnail

함수와 클로저

함수(func)

함수는 func 키워드를 사용해서 정의합니다.
->를 사용해서 함수의 반환 타입을 지정합니다.

func hello(name: String, time: Int) -> Strin {
  var string = ""
  for _ in 0.. < time {
    string += "\(name)님 안녕하세요!\n"
  }
  return string
}

Swift에서는 독특하게 함수를 호출 할 때 파라미터 이름을 함께 써주어야 합니다.

hello(name: "마승현", "time: 3)

만약, 함수를 호출할 때 사용하는 파라미터 이름과 함수 내부에서 사용하는 파라미터 이름을 다르게 사용하고 싶으면 아래와 같이 사용할 수 있습니다.

func hello(to name: String, numberOfTimes time: Int) {
  // 함수 내부에서 'name'과 'time'을 사용합니다.
  for _ int 0..<time {
    print(name)
  }
}

hello(to: "마승현", numberOfTimes: 3)
// 이곳에서 'to'와 'numberOfTimes'를 사용합니다.

파리미터 이름을 _로 정의하면 함수를 호출할 때 파라미터 이름을 생략할 수 있게 됩니다.

func hello(_ name: String, time: Int) {
  // ...
}

hello("마승현", time: 3) // 'name: ' 이 생략되었습니다.

파라미터에 기본 값을 지정할 수도 있습니다.
기본값이 지정된 파라미터는 함수 호출 시 생략할 수 있습니다.

func hello(name: String, time: Int = 1) {
  // ...
}

hello("마승현")

...을 사용하면 개수가 정해지지 않은 파라미터(Variadic Parameters)를 받을 수 있습니다.

func sum(_ numbers: Int...) -> Int {
  var sum = 0
  for number in numbers {
    sum += number
  }
  return sum
}

sum(1, 2)
sum(3, 4, 5)

함수 안에 함수를 작성할 수도 있습니다.

func hello(name: String, time: Int) {
  func message(name: String) -> String {
    return "\(name)님 안녕하세요!"
  }

  for _ in 0..<time {
    print(messae(name: name))
  }
}

심지어 함수 안에 정의한 함수를 반환할 수도 있습니다.

func helloGenerator(message: String) -> (String) -> String {
  func hello(name: String -> String {
    return name + messae
  }
  return hello
}

let hello = helloGenerator(message: "님 안녕하세요!")
hello("마승현")

여기서 핵심은, helloGenerator() 함수의 반환 타입이 (String) -> String 이라는 것입니다.
즉, helloGenerator() 는 "문자열을 받아서 문자열을 반환하는 함수"를 반환하는 함수인 것입니다.

만약 helloGenerator() 안에 정의한 hello() 함수가 여러개의 파라미터를 받는다면 이렇게 사용합니다.

func helloGenerator(message: String) -> (String, String) -> String {
  func hello(firstName: Stringm lastName: String) -> String {
    return lastName+ firstName + message
  }
  return hello
}

let hello = helloGenerator(message: "님 안녕하세요!")
hello("승현", "마")

(String) -> String(String, String) -> String 으로 바뀌었습니다.
문자열 두 개를 받아서 문자열을 반환하는 의미입니다.

함수 안에 작성된 함수를 호출

클로저 (Closure)

클로저(Closure)를 사용하면 바로 위에 작성한 코드를 조금 간결하게 작성할 수 있습니다.
클로저는 중괄호 ({})로 감싸진 "실행 가능한 코드 블럭"입니다.

func helloGenerator(message: String) -> (String, String) -> String {
  return { (firstName: String, lastName: String) -> String in
    return lastName + fristName + message
  }
}

함수와는 다르게 함수 이름 정의가 따로 존재하지 않습니다.
하지만 파라미터를 받을 수 있고, 반환 값이 존재할 수 있다는 점에서 함수와 동일합니다.
즉, 함수는 이름이 있는 클로저라고 할 수 있습니다.

위 함수에서 클로저를 반환하는 코드를 자세히 보자면,
클로저는 중괄호({})로 감싸져있습니다.
그리고 파라미터를 괄호로 감싸써 정의합니다. 함수와 마찬가지로 ->를 사용해서 반환 타입을 명시합니다.
조금 다른 점은 in 키워드를 사용해서 파라미터, 반환 타입 영역과 실제 클로저의 코드를 분리하고 있습니다

{ (firstName: Strng, lastName: String) -> String in
  return lastName + firstName + message
}

클로저의 장점은 간결함유연함에 있습니다.
바로 위에서 작성한 코드는 이해를 돕기 위해 생략 가능한 것들을 하나도 생략하지 않고 모두 적었기 때문에 조금 복잡해보입니다.

하나씩 생략해보겠습니다.

Swift 컴파일러의 타입 추론 덕분에, helloGenerator() 함수에서 반환하는 타입을 가지고 클로저에서 어떤 파라미터를 받고 어떤 타입을 반환하는지 알 수 있습니다.
생략해본다면,

func helloGenerator(message: String) -> (String, String) -> String {
  return { firstName, lastName in
    return lastName + firstName + message
  }
}

여기서 생략을 더 할 수 있습니다.

마찬가지로 타입 추론 덕분에 첫 번째 파라미터가 문자열이고, 두 번째 파라미터도 문자열이라는 것을 알 수 있습니다.
첫 번째 파라미터는 $0, 두 번째 파라미터는 $1로 바꿔서 쓸 수 있습니다.

func helloGenerator(message: String) -> (String, String) -> String {
  return {
    return $1 + $0 + message
  }
}

클로저 내부의 코드가 한 줄이라면, return까지도 생략해버릴 수 있습니다.

func helloGenerator(messae: String) -> (String, String) -> String {
  return { $1 + $0 + message }
}

클로저는 변수처럼 정의할 수 있습니다.

let hello: (String, String) -> String = { $1 + $0 + "님 안녕하세요!" }
hello("승현", "마")

옵셔널로도 정의할 수 있습니다. 체이닝도 가능합니다.

let hello: ((String, String) -> String)?
hello?("승현", "마")

클로저를 변수로 정의하고 함수에서 반환할 수도 있는 것처럼, 파라미터로도 받을 수 있습니다.

func manipulate(number: Int, using block: Int -> Int) -> Int {
  return block(number)
}

manipulate(number: 10, using: { (number: Int) -> Int in
  return number * 2
})

더 생략해서

manipulate(number: 10, using: {
  $0 * 2
})

만약 함수의 마지막 파라미터가 클로저라면, 괄호와 파라미터 이름마저 생략해버릴 수 있습니다.

manipulate(number: 10) {
  $0 * 2
}

이런 구조로 만들어진 예시가 바로 sort()fliter() 입닌다. 함수가 클로저 하나만을 파라미터로 받는다면, 괄호를 아예 쓰지 않아도 됩니다.

let numbers = [1, 3, 2, 6, 7, 5, 8, 4]

let sortedNumbers = numbers.sort{ $0 < $1 }
print(sortedNumbers)
// [1, 2, 3, 4, 5, 6, 7, 8]

let evens = numbers.filter { $0 % 2 == 0 }
print(evens)
// [2, 6, 8, 4]
profile
다른 사람이 만든 것을 소비하는 활동보다, 내가 생산적인 활동을 하는 시간이 더 많도록 생활화 하자.

0개의 댓글