Side-Effect가 발생하지 않는 함수
// 📍 [1] 순수함수: Side-Effect가 발생하지 않는 함수
// 1. name과 greeting 변수에 영향을 받음
var name = "hansol"
var greeting = ""
func makeGreeting() {
greeting = "hello, \(name)"
print(greeting)
}
//makeGreeting() // hello, hansol (greeting 변수도 변경됨)
// 2. 영향을 받는 변수나, 변경되는 변수 없음 (side-Effect 없음)
func makeFPGreeting(name: String) -> String {
return "hello, \(name)"
}
//print(makeFPGreeting(name: "sol")) // hello, sol
// 3. 외부 변수를 사용하지만, immutable data만 이용하기 떄문에 순수함수
let fpGreeting = "hello"
func makeFPGreeting2(name: String) -> String {
return "\(fpGreeting), \(name)"
}
//print(makeFPGreeting(name: "한솔")) // hello, 한솔
// 4. 순수함수로 바꾸기
let nums = [1, 2, 3]
var sum1 = 0
func solution1(_ nums: [Int]) -> Int {
for _ in nums {
sum1 += 1
}
print(sum1)
return sum1
}
// sum1의 값이 계속 변경됨
func solution2(_ nums: [Int]) -> Int {
var sum2 = 0
for _ in nums {
sum2 += 1
}
print(sum2)
return sum2
}
// 외부변수 사용 x, sum2의 값 항상 동일
//solution1(nums) // 3
//solution1(nums) // 6
//solution2(nums) // 3
//solution2(nums) // 3
함수를 파라미터로 받거나 함수를 리턴하는 함수
// 📍 [2] 고차함수: 함수를 파라미터로 받거나 함수를 리턴하는 함수
// 함수는 1급객체임, 함수의 파라미터 값으로 사용하거나 리턴값으로 사용 가능
// 1. 함수를 반환하는 함수
func multiply(_ a: Int) -> (Int) -> Int {
func multi(_ b: Int) -> Int {
return a * b
}
return multi
}
//print(multiply(10)(20)) // 200
let a = multiply(10) // multi 메소드 반환됨, 변수에 할당 가능
// multi 메소드가 클로저로 반환되기때문에, 주변환경을 캡쳐해둠 (에러발생 없이 입력했던 10*30 값이 반환됨)
//print(a(30)) // 300
// 2. 짝수만의 합을 계산하는 코드 만들기
let f: (Int) -> Bool = { $0 % 2 == 0 }
let s: (Int, Int) -> Int = { $0 + $1 }
let n = [1, 2, 3, 4, 5, 8]
func add(_ nums: [Int]) -> Int {
return nums.filter(f).reduce(0, s)
}
//print(add(n)) // 14
함수의 반환값을 다른 함수의 입력값으로 사용
// 📍 [3] 함수의 합성(Composition): 함수의 반환값이 다른 함수의 입력값으로 사용됨
// 반환 타입(Output)과 입력 타입(Input)이 같아야함
// 1. f1 메소드와 f2 메소드가 합성됨
func f1(_ i: Int) -> Int {
return i * 2
}
func f2(_ i: Int) -> String {
return "\(i)"
}
let result = f2(f1(100)) // 200 (String 타입)
// 2. 이렇게 합성도 가능함
func ff(_ func1: @escaping (Int) -> Int, _ func2: @escaping (Int) -> String) -> (Int) -> String {
return { i in
// print(i) // 입력된 정수 값 출력됨 // 100
let res1 = func1(i) // func1 메소드 실행 값 res1 변수에 할당 // 200
let res2 = func2(res1) // func2 메소드의 매개변수로 func1의 실행 값을 할당한 메소드가 반환됨
return res2
// return func2(func1(i))
}
}
let f3 = ff(f1, f2) // 함수의 파라미터로 pf1에는 (Int) -> Int 함수 입력, pf2에는 (Int) -> String 함수 입력
// func2(func1(i)) 값 반환됨
let result2 = f3(100) // 200
// 3. 제네릭으로 구현 가능
func comp<A, B, C>(_ func1: @escaping (A) -> B, _ func2: @escaping (B) -> C) -> (A) -> C {
return { i in
return func2(func1(i))
}
}
let f4 = comp(f1, f2)
let result3 = f4(200) // 400 (String 타입)
// 4. 짝수만의 합을 계산하는 코드, 함수의 합성 이용
func sol<A, B, C>(_ pf1: @escaping (A) -> B, _ pf2: @escaping (B) -> C) -> (A) -> C {
return { i in
return pf2(pf1(i))
}
}
func a(_ ns: [Int]) -> [Int] {
return ns.filter { $0 % 2 == 0 }
}
func b(_ ns: [Int]) -> Int {
return ns.reduce(0, +)
}
let filteredSum = sol(a, b)
func solution(_ nums: [Int]) -> Int {
return filteredSum(nums)
}
여러개의 파라미터를 받는 함수를 하나의 파라미터를 받는 여러개의 함수로 쪼개는 것
// 📍 [4] 커링: 여러개의 파라미터를 받는 함수를 하나의 파라미터를 받는 여러개의 함수로 쪼개는 것
// 커링을 하는 이유? 함수의 Output이 다른 함수의 Input으로 연결되면서 합성된다.
// 함수들이 서로 chain을 이루면서 연속적으로 연결되려면, Output과 Input의 타입과 개수가 같아야한다.
// 함수의 Output은 하나밖에 없으니, Input도 하나씩만 갖도록 한다면 합성하기가 쉬워진다.
// 결국, 함수의 합성을 원활하게 하기 위해서 커링을 사용한다.
// 1. 커링하기
func cur(_ a: Int) -> (Int) -> String {
return { i in
return String(a * i)
}
}
let area = cur(1) // 함수 반환됨
//print(area(5)) // 5 (String 타입)
// 2. n의 배수만을 모아 합을 구하는 함수 filterSum 메소드 커링하기
func filterSum(_ ns: [Int], _ n: Int) -> Int {
return ns.filter({ $0 % n == 0 }).reduce(0, +)
}
func filterSum2(_ n: Int) -> ([Int]) -> Int {
// 1) n의 배수만 찾아야됨
// 2) 찾은 값 다 더해야됨
// 그래서 Int타입의 배열을 Int로 반환하는 로직을 반환한다.
return { i in
let a = i.filter({ $0 % n == 0 })
// print(a)
return a.reduce(0,+)
}
}
func filterSum3(_ nums: [Int], _ r: Int) -> Int {
let filteredR = filterSum2(r)(nums)
return filteredR
}
let number = [1, 2, 3, 4, 5]
let ans = filterSum2(1) // 함수 반환됨
//print(ans(number)) // 15
//print(filterSum3(number, 2)) // 6