목차
- 함수형 프로그래밍이란?
- 순수 함수
- 익명 함수
- 고차 함수
- map
- filter
- reduce
- 불변성
- 불변성이 중요한 이유
- 참조 투명성
- 클로저와 익명함수와의 관계
- Swift 클로저
함수형 프로그래밍이란?
함수형 프로그래밍이란 자료처리를 수학적 함수의 계산으로 취급하고 상태와 가변 데이터를 멀리하는 프로그래밍 패러다임의 하나이다.
Alonze Church가 고안한 람다 계산법을 기반으로 한다.
자세한 내용은 여기에서 확인할 수 있습니다.
함수형과 객체지향의 가장 큰 차이는 함수형은 함수 자체가 일급 객체가 되지만, 객체 지향에서는 클래스가 일급 객체가 됩니다.
그럼 여기서 일급 객체가 무엇인지 알아봐야겠죠?
💡 일급 객체란?
일급 객체(First Class Citizen)란 다른 객체들에 일반적으로 적용 가능한 연산을 모두 지원하는 객체를 가리킨다.
변수에 할당할 수 있고,
인자 값으로 넘기거나,
리턴값으로 받을 수도 있다.
따라서 함수형 프로그래밍에선 함수가 일급 객체가 되기 때문에, 함수를 Int, String 과 같은 자료형처럼 유연하게 다룰 수 있습니다.
순수 함수
순수 함수란 수학적 의미의 함수처럼 입력 값이 동일하면 결과가 동일하게 리턴되는 함수를 의미합니다. 즉 side-effect가 없다는 뜻이고 참조 투명성이란 용어를 사용합니다.
💡 참조 투명성과 부작용
순수 함수로 만들면 함수 외부에 값이나 객체를 참조하거나 의존적으로 동작하지 않기 때문에 참조투명성을 가지고, 부작용이 없다.
반대로 말하면 부작용이 있는 함수는 입력 값이 동일해도 함수 외부에 값에 따라서 다른 값이 리턴한다.
그리고 side-effect가 없다는 건 Thread-safe하다는 뜻이고, 병렬적인 계산이 가능해집니다.
익명 함수
익명 함수란 말 그대로 이름이 없는 함수입니다. 기존의 언어들에선 모든 함수에 이름이 부여되어야만 했습니다. 그러나 함수형 프로그래밍 패러다임을 적용한 언어들은 함수명이 없어도 되는 익명함수를 사용할 수 있습니다.
let closure = { print("hi! i`m anonymous function") }
고차 함수
map, filter, reduce와 같이 함수의 인자로 함수를 전달받고, 리턴 또한 함수로 리턴 가능한 함수를 의미합니다.
map, filter, reduce 함수는 Swift 표준 라이브러리의 컨테이너 타입(Array, Set, Dictionary 등에) 구현되어 있습니다.
map
map 함수는 컨테이너 내부의 기존 데이터를 변형(transform)하여 새로운 컨테이너를 생성합니다.
기존 for 구문 사용
var doubledNumbers = [Int]()
for number in numbers {
doubledNumbers.append(number * 2)
}
print(doubledNumbers) // [0, 2, 4, 6, 8]
map 메소드 사용
doubledNumbers = numbers.map({ (number: Int) -> Int in
return number * 2
})
print(doubledNumbers) // [0, 2, 4, 6, 8]
map 메소드 축약형
doubledNumbers = numbers.map { $0 * 2 }
print(doubledNumbers) // [0, 2, 4, 6, 8]
filter
filter 함수는 컨테이너 내부의 값을 걸러서 새로운 컨테이너로 추출합니다.
기존 for 구문 사용
var filtered: [Int] = [Int]()
for number in numbers {
if number % 2 == 0 {
filtered.append(number)
}
}
print(filtered) // [0, 2, 4]
filter 메소드 사용
// numbers의 요소 중 짝수를 걸러내어 새로운 배열로 반환
let evenNumbers: [Int] = numbers.filter { (number: Int) -> Bool in
return number % 2 == 0
}
print(evenNumbers) // [0, 2, 4]
filter 메소드 축약형
let evenNumbers: [Int] = numbers.filter { $0 %2 == 0 }
print(evenNumbers) // [0, 2, 4]
reduce
reduce 함수는 컨테이너 내부의 요소들을 하나로 통합합니다.
기존 for 구문 사용
let someNumbers: [Int] = [2, 8, 15]
var result: Int = 0
// someNumbers의 모든 요소를 더합니다
for number in someNumbers {
result += number
}
print(result) // 25
reduce 메소드 사용
let someNumbers: [Int] = [2, 8, 15]
let sum: Int = someNumbers.reduce(0, { (first: Int, second: Int) -> Int in
return first + second
})
print(sum) // 25
reduce 축약형
let someNumbers: [Int] = [2, 8, 15]
let sum: Int = someNumbers.reduce(0, +)
print(sum) // 25
불변성
함수 외부의 데이터를 수정하지 않고, 함수의 인자 등 상태를 변경하지 않는 것을 의미합니다.
💡 불변성이 중요한 이유
- 예측 가능성
- 불변 데이터 -> 프로그램의 상태 변화가 없음 -> 동작이 예측 가능함 -> 버그도 없고 테스트도 용이해짐
- 병렬성 및 동시성
- 불변성은 데이터에 대한 Race Condition 방지 -> 병렬 프로그래밍 가능, Thread-Safe
- 리팩토링 및 유지보수 용이성
- 함수들 간의 조립을 통해 로직을 구성하기 때문에 코드가 간결해지고 유지보수에 용이
- 캡슐화 및 추상화
- 불변성은 객체의 내부 상태를 보호하고 캡슐화하여 객체의 상태를 외부에서 직접 조작할 수 없도록 함
좋은 글이 있어 추가합니다! 여기
참조 투명성
위에서 설명했듯이 동일한 입력엔 항상 동일한 결과를 반환하여, side-effect로 부터 안전하다는 뜻으로 해석됩니다.
클로저와 익명함수와의 관계
결론부터 말씀드리면 익명 함수는 클로저의 일종입니다.
흔히 클로저라고 하면, 함수와 같지만 이름이 없는 함수 즉 익명 함수라고 해석하기 쉽습니다.
하지만 func 키워드를 활용한 이름이 존재하는 함수또한 클로저에 포함됩니다.
클로저의 범위는 다음 그림과 같습니다.
Swift 클로저
The Swift Language Guide 에서 소개하는 클로저는 다음과 같습니다.
클로저(Closure)
코드블럭으로 C와 Objective-C의 블럭(blocks)과 다른 언어의 람다와 비슷합니다.
클로저는 어떤 상수나 변수의 참조를 캡쳐해 저장합니다.
정리
- 클로저는 람다계산식(lambda Calculus) 구현체
- 이름 없는 함수(anonymous function)로 리터럴하게 작성가능
- 선언된 범위(scope)에서 접근 가능한 변수를 캡처해서 저장하고 닫힘
- Swift 클로저는 캡처한 변수를 참조(reference)한다.
Swift에서는 함수형 프로그래밍 클로저를 쉽게 만들 수 있고 람다를 활용할 수 있다!
'부스트캠프' 카테고리의 다른 글
네이버 부스트캠프 웹・모바일 9기 챌린지 과정 2주차 회고 (0) | 2024.07.26 |
---|---|
발행-구독 패턴(Publisher-Subscriber Pattern) (0) | 2024.07.25 |
Swift 정규표현식 (0) | 2024.07.23 |
Static Dispatch & Dynamic Dispatch (0) | 2024.07.22 |
네이버 부스트캠프 웹・모바일 9기 챌린지 과정 1주차 회고 (0) | 2024.07.19 |