Swift 뿌수기 - Advanced Operators

Wonbi·2022년 10월 7일
0

Swift 뿌수기

목록 보기
10/12

✅ 학습 내용

📌 고급 연산자

💎 Advanced Operators (고급 연산자)

  • 자신만의 구조체와, 클래스, 및 열거형을 정의할 땐, 이 사용자 정의 타입에 자신만의 표준 스위프트 연산자를 구현하는 게 유용할 수 있다. 스위프트는 이 연산자들의 맞춤식 구현을 쉽게 제공할 뿐더러, 생성한 각 타입마다 이들의 정확한 동작이 무엇인지도 쉽게 결정하게 해준다.
  • 이미 정의된 연산자로 제한된 것도 아니다. 스위프트가 준 자유를 통해, 자신만의 우선 순위 및 결합 법칙 값을 가진, 사용자 정의 중위 (infix) 와, 접두사 (prefix), 접미사 (postfix), 및 할당 (assignment) 연산자를 정의할 수 있다. 이미 정의된 연산자 같이 이 연산자를 코드에서 사용하고 채택할 수 있으며, 심지어 기존 타입을 확장하면 직접 정의한 자신만의 연산자도 지원할 수 있다.

✏️ 우선권과 결합성

  • 연산자 우선권 (precedence) 은 일부 연산자에 다른 것보다 더 높은 우선 순위를 주며 이 연산자를 먼저 적용한다.
  • 연산자 결합성 (associativity) 은 동일 우선권의 연산자를 서로(왼쪽부터 그룹짓거나, 오른쪽부터 그룹지어)묶는 방법을 정의한다. 이는 “자신의 왼쪽 표현식과 결합한다”, 혹은 “자신의 오른쪽 표현식과 결합한다” 는 의미이다.
2 + 3 % 4 * 5
// 이는 17 과 같음
  • 왼쪽에서 오른쪽으로 곧이곧대로 읽으면, 표현식 계산이 다음과 같다고 예상할 수 있다:
    • 2 더하기 3 은 5
    • 5 를 4 로 나눈 나머지는 1
    • 1 곱하기 5 는 5
  • 하지만, 예제처럼 실제 답은 17이지 5가 아니다. 더 높은 우선권인 연산자를 더 낮은 우선권인 것보다 먼저 평가한다.
2 + ((3 % 4) * 5)

💎 중복 정의

✏️ 연산자 메서드

  • 클래스와 구조체는 기존 연산자에 자신만의 구현을 제공할 수 있다. 이를 기존 연산자의 중복 정의 (overloading) 라고 한다.

  • 다음 예제는 이번 계산기 프로젝트에서 사용한 Double Stack Queue기반의 Queue 두개의 요소를 더한 새로운 Queue를 반환하는 +연산자를 중복 정의한다.

extension CalculatorItemQueue {
    static func + (lhs: CalculatorItemQueue, rhs: CalculatorItemQueue) -> CalculatorItemQueue {
        var result = CalculatorItemQueue()
        let operatedResult = zip(lhs.statusQueue, rhs.statusQueue).map { $0 + $1 }
        operatedResult.forEach { result.enqueue($0) }
        return result
    }
}
  • 이 연산자 메서드는 CalculatorItemQueue에 대한 타임 메서드로 정의하며, 메서드 이름은 중복 정의한 연산자 +와 일치한다. 덧셈은 이 큐의 핵심 동작이 아니기 때문에, 타입 메서드를 CalculatorItemQueue구조체의 주 선언부 보다는 CalculatorItemQueue의 익스텐션에 정의한다. 산술 덧셈 연산자가 이항 연산자이기 때문에, 이 연산자 메소드는 CalculatorItemQueue 타입의 입력 매개 변수는 두 개 취하고, 역시 CalculatorItemQueue 타입인, 단일 출력 값을 반환한다.
var test1 = CalculatorItemQueue()
var test2 = CalculatorItemQueue()

for i in 1...10 {
    test1.enqueue(Double(i))
    test2.enqueue(Double(i + 1))
}

print((test1 + test2).statusQueue)
// [3.0, 5.0, 7.0, 9.0, 11.0, 13.0, 15.0, 17.0, 19.0, 21.0]

✏️ 접두사 및 접미사 연산자

  • 클래스와 구조체는 표준 단항 연산자 (unary operators) 도 구현할 수 있다. 단항 연산자는 단일 대상을 연산한다. 자신의 대상 앞에 (-a 처럼) 있으면 접두사 (prefix) 연산자이고 자신의 대상 뒤에 (b! 처럼) 있으면 접미사 (postfix) 연산자이다.

  • 단항 접두사나 단항 접미사 연산자를 구현할 때는 연산자 메소드 선언의 func 키워드 앞에 prefixpostfix 수정자를 작성한다:

extension CalculatorItemQueue {
    static prefix func - (queue: CalculatorItemQueue) -> CalculatorItemQueue {
        var result = CalculatorItemQueue()
        queue.myMap { -$0 }.forEach { result.enqueue($0) }
        return result
    }
}
  • 여기서 myMap은 기본 배열의 map 고차함수와 정확히 동일하게 연산되는 메서드이다.
print((-test1).statusQueue)
// [-1.0, -2.0, -3.0, -4.0, -5.0, -6.0, -7.0, -8.0, -9.0, -10.0]

✏️ 복합 할당 연산자

  • 복합 할당 연산자 (compound assignment operators) 는 할당 = 을 다른 연산과 조합한다. 예를 들어, 덧셈 할당 연산자 += 는 덧셈과 할당을 단일 연산으로 조합한다. 복합 할당 연산자의 왼쪽 입력 매개 변수 타입은 inout 으로 표시하는데, 매개 변수 값을 연산자 메소드 안에서 직접 수정하기 때문이다.
extension CalculatorItemQueue {
    static func += (lhs: inout CalculatorItemQueue, rhs: CalculatorItemQueue) -> CalculatorItemQueue {
        rhs.statusQueue.forEach { lhs.enqueue($0) }
        return lhs
    }
}
  • 마치 배열의 append메서드와 비슷하게 적용되는 로직으로 예제를 작성했다.
print((test1 += test2).statusQueue)
// [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0]

✏️ 같음 비교 연산자

  • 기본적으로, 사용자가 정의한 클래스와 구조체는, 같음 (equal to) 연산자 == 와 같지 않음 (not equal to) 연산자 != 라는, 같음 비교 연산자 (euivalence operators) 를 구현하지 않는다. 대체로 == 연산자는 구현하고, != 연산자는 표준 라이브러리의 기본 구현을 써서 == 연산자의 결과를 반대로 뒤집는다. == 연산자 구현에는 두 가지 방법이 있는데: 스스로 구현할 수도, 또는 많은 타입들에서 스위프트에 통합 구현을 요청할 수도 있다. 두 경우 모두, 표준 라이브러리의 Equatable 프로토콜을 준수하도록 추가한다.
extension CalculatorItemQueue {
    static func == (lhs: CalculatorItemQueue, rhs: CalculatorItemQueue) -> Bool {
        return lhs.statusQueue == rhs.statusQueue
    }
}
  • 위 예제는 == 연산자를 구현하여 배열의 그것과 정확히 동일하게 동작하도록 구현하였다.
print(test1 == test2)
// false

0개의 댓글