-
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