Swift - 함수형 프로그래밍

이한솔·2024년 4월 14일
0

Swift 문법 🍎

목록 보기
32/32

함수형 프로그래밍

순수함수

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


함수의 합성(Composition)

함수의 반환값을 다른 함수의 입력값으로 사용

// 📍 [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)
}


함수의 커링(Currying)

여러개의 파라미터를 받는 함수를 하나의 파라미터를 받는 여러개의 함수로 쪼개는 것

// 📍 [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

0개의 댓글