🌟 비 함수형에서 다른 결과값이 발생하는 이유 : 공장장이 적어둔 숫자를 다른 외부인이 건드리거나 적어두는 사람과 확인하고 수정하는 사람의 타이밍이 엇갈림. (부작용 : 어떤 함수의 동작에 의해 프로그램 내 특정상태가 변경되는 상황) -> 함수형이 주목받는 이유는 이러한 부작용에 의한 문제로부터 보다 자유롭기 때문!
func _calculate(_ method: (Int, Int) -> Int, v1: Int, v2: Int) -> Int {
return method(v1, v2)
}
//함수를 return하는 함수를 return하는 함수
func calculate(_ method: @escaping (Int, Int) -> Int) -> (Int) -> (Int) -> Int {
return { v1 in
return { v2 in
return method(v1, v2)
}
}
}
calculate(+)(10)(3)
let adder = calculate(+)
adder(5)(3)
let add10 = adder(10)
add10(3)
add10(5)
let multi = calculate(*)
multi(2)(3)
let double = multi(2)
double(15)
double(20)
// non-FP
account.deposit()
user.login()
// FP
// 함수를 먼저 쓰고 그 안에 데이터를 집어넣는 것
deposit(account)
user(User)
🌟 가장 큰 차이점은 state가 있냐 없냐의 차이! state가 없도록 하는 것이 FP의 본질이다. 그것이 Side-Effect가 없다고 표현한 것이다.
var i = 1
while i <= 100 {
if i % 3 == 0, i % 5 == 0 {
print("fizzbuzz")
} else if i % 3 == 0 {
print("fizz")
} else if i % 5 == 0 {
print("buzz")
} else {
print("\(i)")
}
i += 1
}
(1...100).forEach { i in
if i % 3 == 0, i % 5 == 0 {
print("fizzbuzz")
} else if i % 3 == 0 {
print("fizz")
} else if i % 5 == 0 {
print("buzz")
} else {
print("\(i)")
}
}
let fizz: (Int) -> String = { i in i % 3 == 0 ? "fizz" : ""}
let buzz: (Int) -> String = { i in i % 5 == 0 ? "buzz" : ""}
(1...100).forEach { i in
let fizzbuzz = fizz(i) + buzz(i)
let output = fizzbuzz.isEmpty ? "\(i)" : fizzbuzz
print(output)
}
let fizz: (Int) -> String = { i in i % 3 == 0 ? "fizz" : ""}
let buzz: (Int) -> String = { i in i % 5 == 0 ? "buzz" : ""}
//fizzbuzz 비교하는 함수
let fizzbuzz: (Int) -> String =
{ i in { s in s.isEmpty ? "\(i)" : s }(fizz(i) + buzz(i)) }
let log: (String) -> () = { print($0) } // 출력하는 함수
(1...100).map(fizzbuzz).forEach(log) // 이것이 바로 선언형!
let fizz = { $0 % 3 == 0 ? "fizz" : ""}
let buzz = { $0 % 5 == 0 ? "buzz" : ""}
//fizzbuzz 비교하는 함수
let fizzbuzz =
{ i in { $0.isEmpty ? "\(i)" : $0 }(fizz(i) + buzz(i)) }
let output = { print($0) } // 출력하는 함수
(1...100).map(fizzbuzz).forEach(output) // 이것이 바로 선언형!
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 fizz = { $0 % 3 == 0 ? "fizz" : nil}
let buzz = { $0 % 5 == 0 ? "buzz" : nil}
let fizzbuzz = { i in fizz(i) + buzz(i) ?? "\(i)" }
let output = { print($0 ?? "") } // 출력하는 함수
(1...100).map(fizzbuzz).forEach(output) // 이것이 바로 선언형!
let i2s: (Int) -> String = { "\($0)" }
let fizz = { $0 % 3 == 0 ? "fizz" : nil }
let buzz = { $0 % 5 == 0 ? "buzz" : nil }
let fizzbuzz = { i in fizz(i) + buzz(i) ?? i2s(i) }
let output = { print($0 ?? "") } // 출력하는 함수
(1...100).map(fizzbuzz).forEach(output) // 이것이 바로 선언형!
🌟 함수가 제네릭으로 구현이 되어 있어서 재사용이 가능하다. 따라서, 개발자가 필요할 때마다 갖다가 쓰면 된다. 개발자는 이미 만들어진 함수형 라이브러리를 가지고 프로그래밍을 하는 것이다.
🌟 위의 i2s 함수에서 생길 버그가 무엇이 있을까? 함수는 짧으면 짧을 수록 즉, 기능의 단위가 작을 수록 생길 버그가 적어진다. 작게 만들어진 함수들을 구성하고 연결해서 개발자가 원하는 기능을 구현하는 것이다.
🌟 신뢰도가 높은 함수들도 구성된 프로그램은 결국 신뢰도가 높을 것이다.