ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Swift를 위한 람다 계산법 핵심
    Apple🍎/Swift 2025. 2. 25. 12:23

    람다 계산법이란?

    람다 계산법은 계산을 함수 형태로 표현하는 형식체계로, 단 세가지의 규칙만으로 모든 계산을 표현할 수 있습니다. 

    이것이 오늘날 Swift를 포함한 현대 프로그래밍 언어의 함수형 프로그래밍 패러다임의 이론적 기반이 되었습니다. 

    람다 계산법 세 가지 핵심 요소 

    1. 변수(Variables) : 가장 기본적인 요소로, 값을 참조하는 이름입니다. Swift에서는 상수나 변수와 같습니다.

    let x = 10
    var name = "Swift"

    2. 추상화(Abstraction) : 함수를 만드는 방법입니다. 람다 계산법에서는 λx.M 형태로 표현하며, "x를 입력으로 받아 M을 계산하는 함수"를 의미합니다.

    λx.x         // 항등 함수 (입력을 그대로 반환)
    λx.(x + 1)   // 입력에 1을 더하는 함수
    
    // 함수 정의
    func identity<T>(_ x: T) -> T { 
        return x 
    }
    func addOne<T>(_ x: T) -> T {
    	return x + 1
    }
    
    // 클로저 표현
    let identity = { (x: Int) -> Int in x }
    let addOne = { (x: Int) -> Int in x + 1 }

    3. 적용(Application): 함수 호출, 함수에 값을 적용하는 것으로, 람다 계산법에서는 (M N) 형태로 표현합니다. 이는 함수 M에 인자 N을 적용한다는 의미입니다.

    (λx.x) 5               // 항등 함수에 5를 적용 → 결과: 5
    (λx.(x + 1)) 2         // 2를 함수에 적용 → 결과: 3
    let result1 = identity(5)        // 5
    let result2 = addOne(2)          // 3

     

    Swift의 함수형 프로그래밍과 람다 계산법

    1. 고차함수 (Higher-Order Functions)

    람다 계산법에서는 함수가 다른 함수를 인자로 받거나 반환할 수 있습니다. Swift에서는 이를 고차함수로 구현하고 있습니다.

    // 함수를 인자로 받는 함수
    λf.λx.(f (f x))    // 함수 f를 입력 x에 두 번 적용하는 함수
    
    // 함수를 두 번 적용하는 함수
    func applyTwice<T>(_ f: @escaping (T) -> T) -> (T) -> T {
        return { x in
            return f(f(x))
        }
    }
    
    let addTwo = { (x: Int) -> Int in x + 2 }
    let addFour = applyTwice(addTwo)
    print(addFour(3))  // 출력: 7 (3+2+2)
    
    // Swift 표준 라이브러리의 고차함수들
    let numbers = [1, 2, 3, 4, 5]
    let doubled = numbers.map { $0 * 2 }           // [2, 4, 6, 8, 10]
    let evens = numbers.filter { $0 % 2 == 0 }     // [2, 4]
    let sum = numbers.reduce(0) { $0 + $1 }        // 15

     

    2. 커링(Currying)

    람다 계산법에서는 여러 인자를 받는 함수를 하나의 인자만 받는 함수들의 체인으로 표현합니다. 이를 커링이라고 합니다.

    λx.λy.(x + y)    // 두 수를 더하는 커링된 함수
    
    // 커링된 함수
    func add(_ x: Int) -> (Int) -> Int {
        return { y in
            return x + y
        }
    }
    
    let add5 = add(5)      // 5를 더하는 함수
    print(add5(3))         // 출력: 8
    print(add(2)(3))       // 출력: 5
    
    // 일반 함수를 커링 함수로 변환
    func curry<A, B, C>(_ function: @escaping (A, B) -> C) -> (A) -> (B) -> C {
        return { a in { b in function(a, b) } }
    }
    
    func multiply(_ a: Int, _ b: Int) -> Int {
        return a * b
    }
    
    let curriedMultiply = curry(multiply)
    let multiplyBy3 = curriedMultiply(3)
    print(multiplyBy3(4))  // 출력: 12

    3. 함수 합성(Function Composition)

    여러 함수를 조합하여 새로운 함수를 만드는 것입니다.

    λf.λg.λx.(f (g x))    // f와 g를 합성하는 함수
    
    // 함수 합성 연산자 정의
    infix operator >>> : AdditionPrecedence
    func >>> <A, B, C>(g: @escaping (A) -> B, f: @escaping (B) -> C) -> (A) -> C {
        return { x in
            return f(g(x))
        }
    }
    
    let square = { (x: Int) -> Int in x * x }
    let addTen = { (x: Int) -> Int in x + 10 }
    
    // 합성 함수: 먼저 제곱한 후 10을 더함
    let squareThenAddTen = square >>> addTen
    print(squareThenAddTen(3))  // 출력: 19 (3^2 + 10)

    4. 클로저(Closure)

    람다 계산법의 함수는 자신이 선언된 환경의 변수를 캡처할 수 있는데, 이것이 현대 프로그래밍의 클로저 개념의 기초가 되었습니다.

    func makeCounter() -> () -> Int {
        var count = 0
        return {
            count += 1
            return count
        }
    }
    
    let counter = makeCounter()
    print(counter())  // 출력: 1
    print(counter())  // 출력: 2
    print(counter())  // 출력: 3

    UIKit과 SwiftUI에서의 실제 적용

    1. UIKit에서의 람다 표현식

    // UIButton 액션
    let button = UIButton()
    button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
    
    // 클로저를 사용한 방식
    let button = UIButton()
    button.addAction(UIAction(handler: { _ in
        print("Button tapped!")
    }), for: .touchUpInside)
    
    // 테이블뷰 셀 구성
    tableView.dataSource = UITableViewDataSource {
        tableView, indexPath in
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
        cell.textLabel?.text = data[indexPath.row]
        return cell
    }

    2. SwiftUI에서의 함수형 접근 

    // SwiftUI는 함수형 프로그래밍 패러다임을 적극적으로 활용하고 있습니다. 
    struct ContentView: View {
        @State private var count = 0
        
        var body: some View {
            VStack {
                Text("Count: \(count)")
                
                Button("Increment") {
                    // 클로저(람다 표현식)
                    count += 1
                }
                
                // 뷰 변환의 연쇄(함수 합성과 유사)
                Circle()
                    .fill(Color.blue)
                    .frame(width: 100, height: 100)
                    .opacity(0.8)
                    .shadow(radius: 10)
            }
        }
    }

    3. Combine 프레임워크

    // Combine 프레임워크는 함수형 리액티브 프로그래밍을 구현하며, 람다 계산법의 개념을 많이 활용합니다:
    let publisher = [1, 2, 3, 4, 5].publisher
    
    publisher
        .map { $0 * 2 }                // 변환
        .filter { $0 > 5 }             // 필터링
        .reduce(0) { $0 + $1 }         // 축소
        .sink { print("Result: \($0)") }  // 구독
        .store(in: &cancellables)

    실용적인 Swift 람다 패턴

    1. 지연 계산 패턴

    // 람다를 사용한 지연 계산
    func heavyComputation() -> Int {
        // 복잡한 계산...
        return 42
    }
    
    func processWithLazy(condition: Bool, computation: @autoclosure () -> Int) -> Int {
        guard condition else { return 0 }
        return computation()  // 조건이 참일 때만 계산 실행
    }
    
    // @autoclosure가 자동으로 클로저로 변환
    let result = processWithLazy(condition: userWantsIt, computation: heavyComputation())

    2. 의존성 주입 패턴

    class NetworkManager {
        let urlSession: URLSession
        let parser: (Data) -> Any
        
        // 함수를 의존성으로 주입
        init(urlSession: URLSession = .shared, 
             parser: @escaping (Data) -> Any = { try? JSONSerialization.jsonObject(with: $0) }) {
            self.urlSession = urlSession
            self.parser = parser
        }
        
        func fetchData(from url: URL, completion: @escaping (Any?) -> Void) {
            urlSession.dataTask(with: url) { [weak self] data, _, error in
                guard let data = data, let self = self else { 
                    completion(nil)
                    return 
                }
                let parsed = self.parser(data)
                completion(parsed)
            }.resume()
        }
    }

    3. 함수형 에러 처리

    enum Result<Success, Failure: Error> {
        case success(Success)
        case failure(Failure)
    }
    
    func fetchUser(id: Int, completion: @escaping (Result<User, NetworkError>) -> Void) {
        // 구현...
    }
    
    // 사용 예시
    fetchUser(id: 123) { result in
        switch result {
        case .success(let user):
            updateUI(with: user)
        case .failure(let error):
            showError(error)
        }
    }

    'Apple🍎 > Swift' 카테고리의 다른 글

    Swift의 컬렉션 타입 : 값 의미론과 실제 구현  (0) 2025.02.28
    클로저 종결판 2  (0) 2025.02.26
    고차함수 for Swift  (0) 2025.02.25
    클로저 종결판  (0) 2025.02.23
    일급객체가 뭔데?  (0) 2025.02.18

    댓글

Designed by Tistory.