Swift

[Swift] 고차함수 더 이상 헷갈리지 않기

양밀루 2025. 6. 4. 20:30

개인적으로 한번 헷갈리기 시작해 영원히 헷갈리는 문법이 몇 가지 있는데, 그 중 첫번째 손님으로 고차함수 map과 reduce 모심

 

✅  각 요소를 변환하는 Map

class MapExamples {
       func basicMapExamples() {
        print("=== MAP 기본 예시 ===")
        
        // 예시 1: 숫자를 2배로
        let numbers = [1, 2, 3, 4, 5]
        
        // Before: for문 사용
        var doubled: [Int] = []
        for number in numbers {
            doubled.append(number * 2)
        }
        print("for문 결과: \(doubled)")
        
        // After: map 사용
        let doubledWithMap = numbers.map { $0 * 2 }
        print("map 결과: \(doubledWithMap)")
        
        print()
        
        // 예시 2: 숫자를 문자열로
        let numberStrings = numbers.map { "숫자: \($0)" }
        print("문자열 변환: \(numberStrings)")
        
        // 예시 3: 타입 변환
        let stringNumbers = ["1", "2", "3", "4", "5"]
        let integers = stringNumbers.map { Int($0)! }
        print("타입 변환: \(integers)")
    }
    
    // 🎯 언제 map을 사용할까?
    func whenToUseMap() {
        print("=== MAP 사용 시점 ===")
        
        // ✅ 사용해야 할 때
        print("✅ 이런 상황에서 map 사용:")
        
        // 1. 각 요소를 다른 형태로 변환
        let ages = [25, 30, 35, 40]
        let ageDescriptions = ages.map { "\($0)세" }
        print("나이 설명: \(ageDescriptions)")
        
        // 2. 객체의 특정 프로퍼티만 추출
        struct Person {
            let name: String
            let age: Int
        }
        
        let people = [
            Person(name: "김철수", age: 25),
            Person(name: "이영희", age: 30),
            Person(name: "박민수", age: 35)
        ]
        
        let names = people.map { $0.name }
        print("이름들: \(names)")
        
        // 3. 계산 결과 배열 만들기
        let prices = [1000, 2000, 3000]
        let taxIncluded = prices.map { Double($0) * 1.1 }  // 세금 포함
        print("세금 포함 가격: \(taxIncluded)")
        
        print()
        
        // ❌ 사용하지 말아야 할 때
        print("❌ 이런 상황에서는 map 사용 안함:")
        
        // 1. 하나의 값으로 합치고 싶을 때 (reduce 사용)
        let sum = ages.reduce(0, +)  // map 아님!
        print("나이 합계: \(sum)")
        
        // 2. 조건에 맞는 것만 걸러내고 싶을 때 (filter 사용)
        let adults = people.filter { $0.age >= 30 }  // map 아님!
        print("성인: \(adults.map { $0.name })")
    }
}

 

 

✅  여러 개를 하나로 합치는 Reduce

class ReduceExamples {
     func basicReduceExamples() {
        print("=== REDUCE 기본 예시 ===")
        
        let numbers = [1, 2, 3, 4, 5]
        
        // 예시 1: 합계 구하기
        // Before: for문 사용
        var sum = 0  // 초기값
        for number in numbers {
            sum = sum + number  // 누적
        }
        print("for문 합계: \(sum)")
        
        // After: reduce 사용
        let sumWithReduce = numbers.reduce(0) { result, number in
            return result + number
        }
        // 또는 더 간단히
        let sumSimple = numbers.reduce(0, +)
        print("reduce 합계: \(sumWithReduce)")
        print("reduce 간단: \(sumSimple)")
        
        print()
        
        // 예시 2: 곱하기
        let product = numbers.reduce(1, *)  // 초기값 1!
        print("곱셈 결과: \(product)")
        
        // 예시 3: 문자열 합치기
        let words = ["안녕", "하세요", "반갑습니다"]
        let sentence = words.reduce("") { result, word in
            result + word + " "
        }
        print("문장: '\(sentence)'")
    }
    
    // 🤔 reduce의 초기값(0) 이해하기
    func understandInitialValue() {
        print("=== REDUCE 초기값 이해하기 ===")
        
        let numbers = [10, 20, 30]
        
        // 초기값이 0일 때
        let sum0 = numbers.reduce(0) { result, number in
            print("  \(result) + \(number) = \(result + number)")
            return result + number
        }
        print("초기값 0으로 합계: \(sum0)")
        print()
        
        // 초기값이 100일 때
        let sum100 = numbers.reduce(100) { result, number in
            print("  \(result) + \(number) = \(result + number)")
            return result + number
        }
        print("초기값 100으로 합계: \(sum100)")
        print()
        
        // 초기값이 1일 때 (곱셈)
        let product = numbers.reduce(1) { result, number in
            print("  \(result) × \(number) = \(result * number)")
            return result * number
        }
        print("초기값 1로 곱셈: \(product)")
        print()
        
        // 초기값이 빈 문자열일 때
        let numberStrings = ["1", "2", "3"]
        let combined = numberStrings.reduce("시작:") { result, str in
            print("  '\(result)' + '\(str)' = '\(result + str)'")
            return result + str
        }
        print("문자열 합치기: '\(combined)'")
    }
    
    // 🎯 언제 reduce를 사용할까?
    func whenToUseReduce() {
        print("=== REDUCE 사용 시점 ===")
        
        // ✅ 사용해야 할 때
        print("✅ 이런 상황에서 reduce 사용:")
        
        let scores = [85, 92, 78, 96, 88]
        
        // 1. 합계, 평균
        let total = scores.reduce(0, +)
        let average = Double(total) / Double(scores.count)
        print("총점: \(total), 평균: \(average)")
        
        // 2. 최대값, 최소값 찾기
        let maxScore = scores.reduce(scores[0]) { max($0, $1) }
        let minScore = scores.reduce(scores[0]) { min($0, $1) }
        print("최고점: \(maxScore), 최저점: \(minScore)")
        
        // 3. 개수 세기 (filter + count 대신)
        let passCount = scores.reduce(0) { count, score in
            count + (score >= 80 ? 1 : 0)
        }
        print("80점 이상: \(passCount)명")
        
        // 4. 복잡한 계산
        let weightedAverage = scores.reduce((0, 0)) { result, score in
            let (totalScore, count) = result
            return (totalScore + score, count + 1)
        }
        print("가중평균 계산용 데이터: \(weightedAverage)")
        
        print()
        
        // ❌ 사용하지 말아야 할 때
        print("❌ 이런 상황에서는 reduce 사용 안함:")
        
        // 1. 각 요소를 변환하고 싶을 때 (map 사용)
        let doubled = scores.map { $0 * 2 }  // reduce 아님!
        print("2배: \(doubled)")
        
        // 2. 조건에 맞는 것만 필터링할 때 (filter 사용)
        let highScores = scores.filter { $0 >= 90 }  // reduce 아님!
        print("90점 이상: \(highScores)")
    }
}

 

 

✅  실제 사용 예시

class PracticalPatterns {
    func commonPatterns() {
        print("=== 자주 사용하는 패턴들 ===")
        
        let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
        
        // 패턴 1: map + filter 조합
        let evenDoubled = numbers
            .filter { $0 % 2 == 0 }  // 짝수만
            .map { $0 * 2 }          // 2배로
        print("짝수를 2배: \(evenDoubled)")
        
        // 패턴 2: map + reduce 조합
        let squareSum = numbers
            .map { $0 * $0 }         // 제곱
            .reduce(0, +)            // 합계
        print("제곱의 합: \(squareSum)")
        
        // 패턴 3: filter + reduce 조합
        let evenSum = numbers
            .filter { $0 % 2 == 0 }  // 짝수만
            .reduce(0, +)            // 합계
        print("짝수의 합: \(evenSum)")
        
        // 패턴 4: 체이닝
        let result = numbers
            .filter { $0 > 5 }       // 5보다 큰 수
            .map { $0 * 3 }          // 3배
            .reduce(0, +)            // 합계
        print("5보다 큰 수를 3배해서 합계: \(result)")
    }
}

'Swift' 카테고리의 다른 글

[Swift] Any와 AnyObject  (0) 2025.06.12
[Swift] 캡처리스트와 순환참조  (1) 2025.06.05
[WWDC] ARC in Swift  (0) 2025.05.30
Struct 접근제어와 Memberwise Initializers  (0) 2025.05.29
[Swift]switch문 대신 if, guard 사용하기  (0) 2025.05.28