[함수형 프로그래밍] 일급 함수
2020. 4. 4. 22:38ㆍiOS
* 이 글은 함수형 프로그래밍 스터디를 하면서 적었던 글로써 사실과 다를 수도 있음..
일급 함수?
- 함수 자체를 값으로써 다룰 수 있다.
- 변수에 할당할 수 있어야 한다.
- 인자로 전달할 수 있어야 한다.
- 반환 값으로 전달할 수 있다.
Function composition (함수의 합성)
- 함수의 반환값이 다른 함수의 입력값으로 사용되는 것
- 함수가 합성되기 위해서는 함수의 반환값과 반환값을 받아들이는 값은 타입이 서로 같아야함
- 순수 함수를 만들어두고 그것을 조합 및 재활용
- 함수 단위의 재활용성이 높아진다
- 코드를 읽기 쉬워진다. ← 이건 아직 잘 모르겠다
- 함수 단위의 테스트가 쉬워진다
- 버그의 감소 ← 일단 써봐야 알겠다.
func increment(_ value: Int) -> Int {
return value + 1
}
func message(with age: Int) -> String {
return "2020년에 \(age)살이 되었다."
}
func compositor(increment: @escaping (Int) -> Int,
message: @escaping (Int) -> String) -> (Int) -> String {
return { (age: Int) in // age -> compositor의 파라메터
return message(increment(age))
}
}
let composited: ((Int) -> String) = compositor(increment: increment(_:), message: message(with:))
let result = composited(26)
print(result) // 27살이 되었다.
- @escaping을 쓰는 이유 - 전달인자로 받은 함수가 탈출 후 사라질 수 있기 때문에
Async Result (비동기 반환)
- 결과는 나중에 생길 때 전달 받고, 프로그램의 수행을 멈추지 않음
- 시간이 걸리는 작업이나 네트워크를 통한 결과를 얻을 경우 비동기 방식으로 구현하는 것이 좋음
- 결과가 리턴값으로 전달 되는 것이 아니라 전달받은 함수를 호출함으로써 전달
- 결과가 발생하는 시점에 수행
func f(_ num: [Int]) -> Int {
sleep(1)
let sum = num.reduce(0, +)
return sum
}
// async를 사용하는 경우
func asyncF(_ num: [Int], onCompletion: @escaping (Int) -> Void) {
DispatchQueue.main.async {
sleep(1)
let sum = num.reduce(0, +)
print(sum)
onCompletion(sum)
}
}
dispatchMain() // 커맨드라인에서는 이거 안하면 async 안돌아감
Currying (커링)
- 여러 개의 파라미터를 받는 함수를 하나의 파라미터를 받는 여러 개의 함수로 쪼개는 것
- 함수들이 서로 chain을 이루면서 연속적으로 연결되려면 output과 input의 타입과 갯수가 같아야한다.
- 함수의 합성을 원활하게 하기 위해서 커링을 사용
func curry<A, B, C>(f: (A, B) -> C) -> (A -> (B -> C)) {
return { a: A in
{ b: B in
return f(a, b) // returns C
}
}
func add(x: Int, y: Int) {
return x + y
}
add(1,2)
func filterWithNum(_ n: Int) -> ([Int]) -> [Int] {
return { b in
return b.filter { $0 % n == 0}
}
}
func filterArray(_ nums: [Int], _ r: Int) -> [Int] {
let filteredArr = filterWithNum(r)
return filteredArr(nums)
}
print(filterArray([1,2,3,4], 2)) // [2, 4]
// 함수의 실행을 늦추고 싶을경우 나중에 인자를 받으면 실행 된다.
// 인자를 고정
Partial Application (부분 적용)
- 여러개의 인자를 받는 함수가 있을 때 일부의 인자를 고정한 함수를 만드는 기법
- 다인수 함수를 생략될 인수의 값을 미리 정해서 더 적은 수의 인수를 받는 하나의 함수로 변형하는 방법
커링과 부분 적용의 차이점
- 커링은 체인의 다음 함수를 리턴하는 반면, 부분 적용은 인수가 더 적은 하나의 함수를 만들어준다.
- 인수가 둘일 경우 사실상 결과가 동일한데, 인수가 셋일 경우 차이점이 명확해진다.여기서 process(x)와 porcess(x)(y)는 인수가 하나인 함수이다. 첫 인수만 커링을 하면 process(x)의 리턴 값은 인수가 하나인(여기서는 y) 또 하나의 함수이다.반면 부분 적용을 사용하여 변환하면 인수 숫자가 적은 함수가 남는다.
- process(x, y, z)의 인수 하나를 부분 적용하면 인수 두개짜리의 process(y, z)가 된다.
- 이 함수의 리턴 값은 또 하나의 일인수 함수이다.
- 예) process(x, y, z) 의 완전한 커링 버전은 process(x)(y)(z)의 체인구조이다.
Memoization (메모이제이션)
- 프로그래밍 언어에 내장되어 반복되는 함수의 리턴 값을 자동으로 캐싱해주는 기능
- 언어 자체에서 제공하는 캐시 기능이기 때문에 최적화된 성능으로 코드를 거의 바꾸지 않고 캐시 혜택을 누릴 수 있다
- 함수형 언어에서는 보편적으로 메모이제이션을 내장해서 제공
- 단, 사용할 때 함수에 부수 효과가 없어야한다. 그렇지 않으면 캐시된 값이 리턴되어도 그 코드를 믿을수 없기 때문이다
13장 클로저 incrementer(값 획득 부분)
참고
'iOS' 카테고리의 다른 글
[함수형 프로그래밍] Maybe & Either (0) | 2020.04.10 |
---|---|
[함수형 프로그래밍] Functor와 Monad (0) | 2020.04.09 |
[함수형 프로그래밍] 순수함수 & 익명함수 & 고차함수 (0) | 2020.04.03 |