예전 포스팅 중 Imperative -> Declarative 예제를 좀 더 진행해보려고 합니다.
아래는 전에 보여드렸던 Imperative vs Declarative 예제입니다.
let fizz: (Int) -> String = { i in i % 3 == 0 ? "fizz" : "" }
let buzz: (Int) -> String = { i in i % 5 == 0 ? "buzz" : ""}
let fizzbuzz = { i in { $0.isEmpty ? "\(i)": $0}(fizz(i) + buzz(i)) }
let output = { print($0) }
(1...100).map(fizzbuzz).forEach(output)
1. fizz(i) + buzz(i) 부분을 개선하고자 아래와 같이 + 함수를 만들어 주고, "\(i)" 부분을 i2s라는 함수로 만들어보겠습니다.
func + (_ s1: String?, _ s2: String?) -> (String?) {
if s1 == nil, s2 == nil { return nil }
if s1 != nil, s2 == nil { return s1 }
if s1 == nil, s2 != nil { return s2 }
return s1! + s2!
}
let i2s: (Int) -> String = { "\($0)" }
.
.
let fizzbuzz = { i in fizz(i) + buzz(i) ?? i2s(i) }
.
.
(1...100).map(fizzbuzz).forEach(output)
+ 함수와 i2s의 함수를 만들어주면서 fizzbuzz 함수는 더욱 더 간결하게 바뀌었습니다!
2. 이번에는 배열과 함수 받아 forEach와 함께 함수를 실행하는 iterate 함수를 만들어 보겠습니다.
func iterate<A>(_ arr: [A], _ f: ((A) ->())) {
arr.forEach({ f($0) })
}
iterate(Array(1...100), {i in output(fizzbuzz(i))})
생성한 iterate 함수로 1부터 100까지의 배열를 받아 그 배열을 순서대로 output 함수를 실행시킵니다.
output 함수는 fizzbuzz에 forEach의 결과값 i를 넣은 결과 값을 리턴해줍니다!
근데, output(fizzbuzz()) 형태는 뭔가 거북합니다. 실행되는 순서 반대로 되어 있잖나요?
3. 그럼 f함수 g함수 두개를 받아서 f함수를 g함수의 부분 함수로 만드는 pipe 합성함수를 만들어 보겠습니다.
func pipe <A, B, C>(_ f: @escaping (A) -> B, _ g: @escaping (B) -> C) -> (A) -> C {
return { a in g(f(a))}
}
iterate(Array(1...100), pipe(fizzbuzz, output))
기존 g(f(x)) 형태를 pipe함수로 pipe(f(x), g(y)) 로 변경하였습니다.
이 함수는 연결되어 있다. (순차적으로 실행한다.) 라는 생각으로 개발할 수 있겠네요!
4. 자, pipe 함수를 infix ~> 로 변경해 봅시다.
precedencegroup Action {
associativity: left
}
infix operator ~>: Action
func ~> <A, B, C>(_ f: @escaping (A) -> B, _ g: @escaping (B) -> C) -> (A) -> C {
return { a in g(f(a))}
}
iterate(Array(1...100), fizzbuzz ~> output)
좀 더 보기 편해졌습니다! pipe(f(x), g(y)) 형태를 ~> 로 간편하게 변경했습니다.
5. 마지막으로, 새로운 함수 cap 를 추가해 봅시다 ^^;
let cap: (String?) -> String? = { $0?.capitalized }
iterate(Array(1...100), fizzbuzz ~> cap ~> output)
훨씬 더 이해해하기 쉬워졌죠?
fizzbuzz를 실행하고 cap를 실행하고 output를 실행하고 좀 더 Declarative 하게 작성할 수 있도록 변경되었습니다!
5. 결론
-> No Side Effect, Bug를 줄일 수 있고, 이해하기 쉽게 프로그래밍 할 수 있다는 장점이 있습니다.
참고링크
https://iosdevkor.github.io/let_us_go_2018_spring_review/ 에서 "곰튀김님의 Functinal Programing이 뭐하는 건가요?"
http://azsha.tistory.com/99?category=743068 [FP] Functional Programing 용어 정리
'Programer > iOS' 카테고리의 다른 글
[UI] TableView에서 이미지 크기에 따라 Cell 크기 조정하기 (0) | 2019.01.18 |
---|---|
[Swift] StoryBoard 로컬라이즈(지역화) 쉽게하기 (0) | 2018.08.24 |
[FP] 함수 & 고차함수 vs 클로저 (0) | 2018.07.12 |
[FP] Functional Programing 용어 정리 (0) | 2018.07.10 |
[Swift] Class와 Struct 무엇을 써야 할까요? (Class VS Struct) (0) | 2018.07.05 |