본문 바로가기
Xcode/Swift - PlayGround

PlayGround) Generic - Closure의 확장

by 후르륵짭짭 2021. 4. 11.
728x90
반응형

 

 

 

안녕하세요! 후르륵짭짭 입니다!

최근 회사의 Swift 고급 강의를 들어서 큰 도움을 받아, 공부할 겸 하나씩 정리해보려고 합니당!

이번에는 첫 강의 복습 글이기 때문에 쉬운 내용으로 작성하려 합니다. (글쓰기가 힘들기도 해요 ㅎㅎㅎ)

그리고 앞으로 제가 좋아하는 뮤직비디오랑 내용 정리 글을 올린 후에 간단한 잡 생각을 작성하려고 합니다.

나중에 제가 이 글을 봤을 때, 그냥 일기 느낌으로 보려고요 ㅎㅎㅎ.

www.youtube.com/watch?v=cHkDZ1ekB9U

< 노래도 좋고 뮤비도 재미있고 두 배우가 너무 잘 어울립니다.>

 

** Generic - Closure의 확장 **

Closure는 함수이지만 다양한 형태로 정의를 쉽게 해줄 수 있는 함수 입니다.

2020.12.08 - [Xcode/Swift - PlayGround] - PlayGround ) Closure의 기능은 무엇인가?

PlayGround ) Closure의 기능은 무엇인가?

안녕하세요! 후르륵짭짭 입니다. 이번에는 Closure에 대한 저의 착오를 말씀 드릴려고 합니다. 전 Closure를 값을 받아 수행하는 함수라고 생각했습니다. 그러니깐 함수 내부에서 클로저를 수행한다

hururuek-chapchap.tistory.com

예전에 저 글에서 클로저는 기능의 정의를 한 후에 사용하는 것이라고 말씀드렸습니다.

Generic와 Closure의 기본적인 내용을 알고 있다면 이번에 정리하는 내용은 쉽게 이해 할 수 있을 겁니다.

 

** 이 내용이 필요한 이유 **
protocol Validator {
    associatedtype Value
    
    func validate(_ value : Value) -> Bool
}

struct MiniSumValidate : Validator {
    
    let miniCount : Int
    
    func validate(_ values: [Int]) -> Bool {
        return values.reduce(0, +) < miniCount
    }
    
}

struct MaxSumValidate : Validator {
    
    let maxCount : Int
    
    func validate(_ values: [Int]) -> Bool {
        return values.reduce(0, +) > maxCount
    }
    
}

만약 Int 배열에 들어가 있는 값이 최소값 보다 작은지, 최대값 보다 큰지 판단해주는 인스턴스를 생성한다면

위에 처럼 같은 구조를 만들기 위해 Protocol을 만들어 주고 MiniSumValidate와 MaxSumValidate를 만들어줬습니다.

그리고 사용 할 때는 아래 처럼 생성해서 사용 해주면 됩니다.

let list : [Int] = [10,30,12,43,54,12]

let validator = MiniSumValidate(miniCount: 20)
print("MiniSumValidate 의 결과는 : ",validator.validate(list))

let list2 : [Int] = [12,13,14,15,20,14]

let validator2 = MaxSumValidate(maxCount: 30)
print("MaxSumValidate 의 결과는 : ", validator2.validate(list2))

이렇게 Protocol을 사용해서 비슷한 형태의 인스턴스를 생성 할 수 있습니다.

그리고 Protocol을 준수하기 때문에 원하는 구조를 찍어낼 수 있습니다.

하지만 이러면 비슷한 구조의 인스턴스를 매번 생성해줘야하기 때문에 비효율적 입니다.

이런 문제를 해결하기 위해 제너릭과 클로저를 함께 사용했습니다.

 

** 클로저와 제너릭의 활용 **
struct Validator<T>{
    let validate : (T) -> Bool
    
    init(validate : @escaping (T) -> Bool){
        self.validate = validate
    }
}

이렇게 제너릭을 사용해서 어떤 타입이 들어와도 사용할 수 있도록 해줍니다.

init()을 통해 T 타입을 받아서 Bool을 반환해주는 함수를 정의 한 다음  self.validate에 넣어줍니다.

let minSumValidator = Validator(validate: { (lists : [Int]) -> Bool in
    return lists.reduce(0, +) < 20
})

let maxSumValidator = Validator(validate: { (lists : [Int]) -> Bool in
    return lists.reduce(0, +) > 30
})

이렇게 해주면 클로저를 통해서 매번 self.validate를 다르게 만들어 줄 수 있습니다.

Validate 인스턴스는 그대로 사용 했는데, 내부의 클로저를 새롭게 정의 해줌을 통해 좀 더 간단한게

비슷하지만 다른 인스턴스를 만들 수 있게 됩니다.

let list : [Int] = [10,30,12,43,54,12]

let minSumValidator = Validator(validate: { (lists : [Int]) -> Bool in
    return lists.reduce(0, +) < 20
})
//클로저를 이용하면 언떤 것이 인자로 들어가는지 안 알려주기 때문에 직접 알아서 넣어줘야한다.
//만약 인자가 있는데 안 넣었을 경우 Function이라고 나온다.
print(minSumValidator.validate(list))

let maxSumValidator = Validator(validate: { (lists : [Int]) -> Bool in
    return lists.reduce(0, +) > 30
})

print(maxSumValidator.validate(list))

위에 처럼 사용해주면 되는데, 클로저 같은 경우는 어떤 인자가 들어가는지 자동으로 안 알려주기 때문에, 직접 넣어줘야합니다.

인자를 빼고 넣어주면 그냥 Function 이라는 값이 나오게 됩니다.

 

** 클로저를 반환! **
extension Validator{
    
    func combine(_ other : Validator<T>) -> Validator<T> {
        
        let newValidate = Validator(validate: { (input) -> Bool in
            
            print("input : \(input)")
            //init에서 validate를 생성 해줄 때 이미 T가 정해진다.
            //따라서 input : [Int] 가 되지 않는다.
            let result = validate(input)
            let result2 = other.validate(input)
            
            return result && result2
            
        })
        
        return newValidate
        
    }
    
}

위의 내용을 이해 했다면 위의 코드도 충분히 이해가 될 겁니다.

새로운 클로저를 반환해주는 겁니다.

기존의 인스턴스에 존재하던 self.validate와 새로운 인자로 들어온 validate를 합쳐서 

새로운 Validate 인스턴스를 반환해주는 겁니다.

(참고로 input : [Int]가 안되는 이유는 이미 init()에서 T에 대한 정의를 내려줬기 때문에 input의 타입도 정해지게 됩니다.)

let combinValidate = minSumValidator.combine(maxSumValidator)

//이미 minSumValidator를 생성할 때 [Int]로 만들어 줬기 때문에
//Cannot convert value of type 'String' to expected element type 'Array<Int>.ArrayLiteralElement' (aka 'Int')
//따라서 이렇게 오류가 발생한다.
//combinValidate.validate(["!"])

print(combinValidate.validate([12,3,4,5,3,2,12]))

그리고 이렇게 사용이 가능합니다.

이렇게 사용하면 기존의 내용들을 합쳐서 새로운 기능을 가진 함수를 만들 수 있기 때문에, 사용성 측면에서도 효율적 일 것 같습니다.

 

** 20.04.11 - 잡 생각 **

진지함을 잘 몰라서 가끔 의도하지 않는 상처를 줄 때가 있습니다.

그런데 누구는 항상 남이 상처 받지 않게 말을 이쁘게 하더군요.

그래서 그 모습을 보면서 배우고 저도 달라지려고 합니다.

감사합니다.

 

728x90
반응형

댓글