reduce 메커니즘은 데이터 컬렉션에 대한 기능적 작업에 해당하며, 스위프트는 이것을 기능적 언어(map-reduce, fold)에서 가져왔습니다. 역사적으로 이 기능은 모든 컬렉션을 단일 집계 값(예: 합계, 곱셈, 문자열 결합 등)으로 변환할 수 있게 해주며, 모든 요소를 순회하며 결과를 누적합니다. 여기서 해결하는 기본 문제는 수동 루프 대신 데이터 집계를 간결하고 읽기 쉽게, 그리고 버그 없이 처리하는 것입니다.
스위프트에서 reduce는 컬렉션 메서드로 정의되어 있습니다:
func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result
이는 초기(시작) 값을 지정하고, 이후 각 요소와 현재의 누산기에서 새로운 집계 값을 반환하는 함수를 작성해야 함을 의미합니다.
코드 예:
let numbers = [1, 2, 3, 4] let sum = numbers.reduce(0) { $0 + $1 } // 10 let joined = numbers.reduce("") { $0 + String($1) } // "1234"
주요 특징:
빈 컬렉션에서 reduce는 어떻게 작동하나요?
Reduce는 컬렉션의 각 요소에 적용됩니다. 컬렉션이 비어 있으면 초기(시작) 값이 반환되며, 클로저 호출은 발생하지 않습니다.
코드 예:
let empty: [Int] = [] let sum = empty.reduce(100) { $0 + $1 } // 100
reduce를 사용하여 초기 컬렉션을 변경할 수 있나요?
아니요, reduce는 순수 함수(pure function)로, 초기 컬렉션을 변경하지 않습니다. 모든 변경은 오직 누산기에 대해서만 발생합니다.
reduce와 reduce(into:)의 차이는 무엇인가요?
reduce(into:)는 각 반복 시 누산기를 변형할 수 있게 하며, 값 타입(value type)에 대해 더 효율적으로 작동합니다. 값 타입에서 새 복사본(copy-on-write)의 생성이 비용이 많이 들기 때문입니다.
코드 예:
let nums = [1, 2, 3] let squares = nums.reduce(into: []) { (result: inout [Int], item) in result.append(item * item) } // squares == [1, 4, 9]
개발자는 products 배열에 저장된 제품 가격을 합산하고 싶어 했습니다. 초기 값 0.0으로 reduce를 사용했지만, 클로저가 할인으로 인해 때때로 음수의 합계를 반환하여 부정확한 최종 결과와 세금 계산 문제를 초래했습니다.
장점:
단점:
id에 따라 엔티티 배열에서 캐시를 생성하기 위해 reduce(into:)를 사용했습니다:
let objects: [Entity] = ... let cache = objects.reduce(into: [:]) { $0[$1.id] = $1 }
장점:
단점: