Closure와 고차함수

Closure

  • 클로저란 코드의 블럭이자, 일급 객체로 완벽한 역할을 할 수 있다.

    일급 객체란 전달 인자로 보낼 수 있고, 변수/상수 등으로 저장하거나 전달할 수 있으며, 함수의 반환 값이 될 수도 있다. 실제 우리가 알고 있는 함수는 클로저의 한 형태로, 이름이 있는 클로저이다.

  • 클로저의 기본 타입은 다음과 같다.
    { (매개 변수들) -> 반환 타입 in
     실행 코드
    }
    
  • 클로저는 매개 변수, 반환 타입을 생략할 수 있다. 함수의 매개 변수로 넘어가는 용도로 가장 많이 쓰인다.
let reversed: [String] = names.sorted() { (left: String,
right: String) -> Bool in
  return left > right
} 

// 메서드의 소괄호 생략
let reversed: [String] = names.sorted { (left: String,
right: String) -> Bool in
  return left > right
}

// 클로저의 매개 변수 타입과 반환 타입을 생략
let reversed: [String] = names.sorted { (left, right) in
  return left > right
}

// 단축 인자 이름 사용
let reversed: [String] = names.sorted {
  return $0 > $1
} 

// 암시적 반환 표현 사용
let reversed: [String] = names.sorted { $0 > $1 }

// 매개변수 생략
let reversed: [String] = names.sorted(>)

고차함수 (Higher-Order Function)

  • 고차 함수란 하나 이상의 함수를 인자로 취하거나 함수를 결과로 반환하는 함수이다.
  • Swift의 유용한 고차 함수로 map, filter, reduce 등이 있다.

map

  • map은 컬렉션 내부의 기존 데이터를 변형해서 새로운 컬렉션을 생성하는 함수이다.
  • 내부의 자료에 변형을 가하기 위해 함수의 각 요소에 함수를 적용해서 새로운 컬렉션을 만들어준다.
  • 잘 사용하면 for 문을 사용하지 않고도 작업할 수 있다.
let numbers: [Int] = [0, 1, 2, 3, 4]
var doubledNumbers: [Int] = [Int]()
var strings: [String] = [String]()

 for for number in numbers {
  doubledNumbers.append(number * 2)
  strings.append("(number)")
}

print(doubledNumbers) // [0, 2, 4, 6, 8]
print(strings) // ["0", "1", "2", "3", "4"]
// map 메서드 사용
doubledNumbers = numbers.map({ (number: Int) -> Int in
  return number * 2
})
strings = numbers.map({ (number: Int) -> String in
  return "(number)"
})

print(doubledNumbers) // [0, 2, 4, 6, 8]
print(strings) // ["0", "1", "2", "3", "4"]
/ 매개변수, 반환 타입, 반환 키워드 (return) 생략
// 후행 클로저
doubledNumbers = numbers.map { $0 * 2 }
print(doubledNumbers) // [0, 2, 4, 6, 8]

filter

  • filter는 컨테이너 내부의 값을 걸러서 추출하는 함수이다.
let numbers: [Int] = [0, 1, 2, 3, 4, 5]
var evenNumbers: [Int] = [Int]()

// for 구문 사용
for number in numbers {
  if number % 2 != 0 { continue }
  evenNumbers.append(number)
}

print(evenNumbers) // [0, 2, 4]
// filter 메서드 사용
let numbers: [Int] = [0, 1, 2, 3, 4, 5]
let evenNumbers: [Int] = numbers.filter { (number: Int) -> Bool in return number % 2 == 0 }
print(evenNumbers) // [0, 2, 4]

/ 매개변수, 반환 타입, 반환 키워드 (return) 생략
// 후행 클로저
let oddNumbers: [Int] = numbers.filter {
  $0 % 2 != 0
}
print(oddNumbers) // [1, 3, 5]

reduce

  • reduce는 컨테이너 내부의 컨텐츠를 하나로 통합해준다.
let numbers: [Int] = [2, 8, 15]
var sum: Int = 0

// for 구문 사용
for number in numbers {
  sum += number
}

print(sum) // 25
// reduce 메서드 사용
let numbers: [Int] = [2, 8, 15] // 0

// 초깃값이 0이고 정수 배열의 모든 값을 더함
let sum: Int = numbers.reduce(0, { (first: Int, second:
Int) -> Int in
  print("(first) + (second)")
  return first + second
})

print(sum) // 25 !
let numbers: [Int] = [2, 8, 15]

// 초깃값이 0이고 정수 배열의 모든 값을 뺌
let subtract: Int = numbers.reduce(0, { (first: Int,
second: Int) -> Int in
  print("(first) - (second)")
  return first - second
})

print(subtract) // -25
// 초깃값이 3이고 정수 배열의 모든 값을 더함
let sumFromThree = numbers.reduce(3) { $0 + $1 }
print(sumFromThree) // 28


/* var sum: Int = 3
for number in numbers {
  sum += number
}
*/

sum이라는 let 상수에 값을 더하는데, 뒤에 값을 바꾸는 로직이 없으므로 상수를 사용할 수 있다. 앞서 for 문에서는 var 변수로 선언해야 하기 때문에 나중에 의도하지 않게 값을 변화할 여지가 있으므로 고차원 함수로 사용하면 이런 실수를 줄일 수 있다는 장점이 있다.

참고 : https://academy.realm.io/kr/posts/closure-and-higher-order-functions-of-swift/


Choi Hyowon

열심히 공부 중 입니다.