반응형

[최종 수정일 : 2018.09.13]

원문 : 애플 개발 문서 Swift 4.2 Language Guide - Advanced Operators

고급 연산자(Advanced Operators)

기본 연산자(Basic Operators)에서 설명된 연산자 외에, Swift는 더 복잡한 값을 다루기 위한 몇가지 고급 연산자를 제공합니다. 여기에는 C와 Objective-C에서 익숙한 모든 비트단위(bitwise)와 비트 쉬프트(bit shifting) 연산자가 포함됩니다.

C에서의 산술(arithmetic) 연산자와는 다르게, Swift에서의 산술 연산자는 기본적으로 오버플로우(overflow) 하지 않습니다. 오버플로우(overflow) 동작은 오류가 발생합니다. 오버플로우 동작 선택을 선택하기 위해, 오버플로우 추가 연산자(&+)처럼, 기본적으로 오버플로우되는 Swift의 두번재 산술연산자 set을 사용합니다. 이러한 모든 오버플로우 연산자는 &으로 시작합니다.

자신만의 구조체, 클래스, 열거형을 정의할때, 사용자정의 타입에 대해 자신만의 표준 Swift연산자의 구현을 제공하는 것이 유용할 수 있습니다. Swift는 이러한 연산자의 맞춤형 구현을 제공하고 자신이 만든 각 타입에 대한 행동이 무엇인지 결정해야하는 것을 쉽게 해줍니다.

미리 정의된 연산자에만 국한되지 않습니다. Swift는 사용자정의 우선순위와 연관된 값으로 자신만의 사용자정의 중위(infix), 전위(prefix), 후위(postfix)와 할당연산자(=)를 정의하는 자유를 줍니다. 이러한 연산자들은 미리 정의된 모든 연산자처럼 코드에서 사용되고 채택될수 있고, 여러분이 정의한 사용자정의 연산자를 지원하기 위해 기존 타입을 확장할 수 있습니다.

비트단위 연산자(Bitwise Operators)

비트단위 연산자(Bitwise operators)는 데이터 구조에서 개별적인 원시 데이터 비트를 관리하는 것이 가능합니다. 이것들은 그래픽 프로그래밍과 디바이스 드라이버 생성과 같은, 로우 레벨(low-level) 프로그래밍에서 사용됩니다. 비트단위 연산자는 사용자정의 프로토콜을 통한 통신을 위해 데이터를 인코딩(encoding)하고 디코딩(decoding)하는 것처럼, 외부 소스의 원시 데이터로 작업할때 유용할 수 있습니다.

아래 설명된 것처럼, Swift는 C에 있는 모든 비트단위 연산자를 지원합니다.

NOT 비트단위 연산자(Bitwise NOT Operator)

NOT 비트단위 연산자(bitwise NOT operator)(~)은 숫자에 있는 모든 비트들을 반전(inverts) 시킵니다.

NOT 비트단위 연산자는 전위 연산자이고, 공백없이 연산하는 값 바로 앞에 나타납니다.

let initialBits: UInt8 = 0b00001111
let invertedBits = ~initialBits  // equals 11110000

UInt8 정수형은 8개의 비트를 가지고 0부터 255사이의 값을 저장할 수 있습니다. 이 예제는 UInt8 정수형을 처음 4개의 비트는 0으로, 두번째 4개의 비트에는 1로 설정하는 2진수(binary) 값 00001111으로 초기화 합니다. 이것은 10진수(decimal) 값 15와 같습니다.

NOT 비트단위 연산자는 initialBits와 같지만, 모든 비트가 반전된 상수 invertedBits를 만드는데 사용됩니다. 0은 1이되고, 1은 0이 됩니다. invertedBits의 값은 부호없는 10진수(decimal) 값 240과 같은 11110000입니다.

AND 비트단위 연산자(Bitwise AND Operator)

AND 비트단위 연산자(bitwise AND operator)(&) 는 두 숫자들의 비트를 결합합니다. 이것은 입력된 숫자 모두(both) 1인 경우에만 1로 설정하는 새로운 비트 값을 반환합니다.

아래 예제에서, firstSixBits와 lastSixBits값 모두 중간에 있는 4개의 비트가 1입니다. AND 비트단위 연산자는 부호없는 10진수(decimal) 값 60과 같은 00111100 숫자를 만들기 위해 결합합니다.

let firstSixBits: UInt8 = 0b11111100
let lastSixBits: UInt8  = 0b00111111
let middleFourBits = firstSixBits & lastSixBits  // equals 00111100

OR 비트 연산자(Bitwise OR Operator)

OR 비트 연산자(|)는 두 숫자의 비트들을 비교합니다. 이 연산자는 입력된 숫자들의 비트에서 하나라도 1인 경우에, 1을 설정하는 새로운 비트 값을 반환합니다.

아래 예제에서, someBits와 moreBits의 값은 1로 설정된 다른 비트를 가집니다. OR 비트단위 연산자는 부호없는 10진수 254와 같은, 11111110을 만들기 위해 결합합니다.

let someBits: UInt8 = 0b10110010
let moreBits: UInt8 = 0b01011110
let combinedbits = someBits | moreBits  // equals 11111110

XOR 비트단위 연산자(Bitwise XOR Operator)

XOR 비트단위 연산자(bitwise XOR operator) 또는 "배타적인 OR 연산자(exclusive OR Operator)" (^)는 두 숫자들의 비트를 비교합니다. 이 연산자는 입력된 비트들이 다르면 1을 설정하고 입력한 비트들이 같으면 0을 설정하는, 새로운 비트 값을 반환합니다.

아래 예제에서, firstBits와 otherBits의 값은 서로 다른 비트에 1을 설정합니다. XOR 비트단위 연산자는 출력값에서 비트들 모두 1로 설정합니다. firstBits와 otherBites에 있는 모든 다른 비트들은 모두 일치하고 출력값에서 0으로 설정합니다.

let firstBits: UInt8 = 0b00010100
let otherBits: UInt8 = 0b00000101
let outputBits = firstBits ^ otherBits  // equals 00010001

비트단위 왼쪽과 오른쪽 시프트 연산자(Bitwise Left and Right Shift Operators)

비트단위 왼쪽 시프트 연산자(Bitwise left shift operators)(<<)와 비트단위 오른쪽 시프트 연산자(Bitwise right shift operators)(>>)는 숫자에 있는 모든 비트를 특정 숫자만큼, 왼쪽이나 오른쪽으로 이동시키며, 아래 정의된 규칙을 따릅니다.

비트단위 왼쪽과 오른쪽 시프트(Bitwise left and right shifts)는 정수를 곱하거(multiplying)나 나누는(dividing) 효과를 가지고 있습니다. 정수의 비트를 왼쪽으로 한칸씩 밀면(shiping) 값은 두배(doubles)가 되며, 오른쪽으로 한칸씩 밀면 값은 반절(halves)이 됩니다.

부호없는 연산자에 대한 시프트 동작(Shifting Behavior for Unsigned Integers)

부호 없는 정수형에 대한 비트 시프트(bit-shifting) 동작은 아래와 같습니다.

  1. 기존 비트들은 요청된 숫자만큼 왼쪽이나 오른쪽으로 이동됩니다.
  2. 정수형의 범위를 넘어서 이동된 모든 비트들은 버려집니다.
  3. 원본 비트들을 왼쪽이나 오른쪽이나 이동시킨 후에 비어 있는 곳에 0이 삽입됩니다.

이러한 접근법을 논리적인 시프트(logical shift)이라고 합니다.

아래 그림은 11111111 << 1(11111111이 왼쪽으로 1씩 이동)와 11111111 >> 1(11111111이 오른쪽으로 1씩 이동) 결과를 보여줍니다. 파란색 숫자는 이동되며, 회색 숫자는 버려지고, 오랜지색은 0이 삽입됩니다.

다음은 Swift코드로 비트 시프트(shifting)하는 것을 보여줍니다.

let shiftBits: UInt8 = 4   // 00000100 in binary
shiftBits << 1             // 00001000
shiftBits << 2             // 00010000
shiftBits << 5             // 10000000
shiftBits << 6             // 00000000
shiftBits >> 2             // 00000001

다른 데이터 타입으로 인코딩하고 디코딩하기 위해 비트 시프트를 사용할 수 있습니다.

let pink: UInt32 = 0xCC6699
let redComponent = (pink & 0xFF0000) >> 16    // redComponent is 0xCC, or 204
let greenComponent = (pink & 0x00FF00) >> 8   // greenComponent is 0x66, or 102
let blueComponent = pink & 0x0000FF           // blueComponent is 0x99, or 153

이 예제는 분홍색 CSS(Cascading Style Sheets) 색상 값을 저장하기 위해 UInt32 상수 pink를 사용합니다. CSS 색상 값 #CC6699는 Swift1 6진수(hexadecimal) 숫자 0xCC6699로 표현됩니다. 이 색상은 AND 비트단위 연산자(&)와 비트단위 왼쪽 시프트 연산자(>>)를 사용해서 빨강(CC), 녹색(66), 파랑(99) 성분으로 분해됩니다.

빨강 구성요소(component)는 0xCC6699와 0xFF0000 숫자들간에 AND 비트단위 연산을 수행해서 얻어집니다. 0xFF0000에서 0은 두번재와 세번째 바이트들에 마스크(mask) 효과로 6699가 무시되고 결과적으로 0xCC0000이 남게됩니다.

이 숫자는 오른쪽으로 16자리만큼 밀리게됩니다(shifted) (>> 16). 16진수 숫자에서 각 문자들은 8비트를 사용하며, 오른쪽으로 16만큼 이동하면 0xCC0000이 0x0000CC로 변경될 것입니다. 이것은 10진수 값 204을 가지는 0xCC와 같습니다.

비슷하게, 녹색 구성요소는 0xCC6699와 0x00FF00숫자간에 AND 비트단위 연산을 수행해서 결과 값 0x6600이 얻어집니다. 그리고나서 이러한 출력 값은 오른쪽으로 8자리만큼 밀리게 되며(shifted), 10진수 값 102인 0x66의 값이 주어집니다.

마지막으로, 파랑 구성요소는 0xCC6699와 0x0000FF숫자간에 AND 비트단위 연산을 수행해서 결과값 0x000099가 얻어집니다. 0x000099는 이미 10진수 값 153인 0x99와 같으므로 오른쪽으로 밀(shift) 필요가 없습니다.

부호있는 정수형에 대한 시프트 동작(Shifting Behavior for Signed Integers)

부호 있는 정수형은 2진수로 표현되는 방식 때문에,부호가 없는(unsigned) 정수형 보다 부호가 있는(signed) 정수형의 시프트(shifting) 동작은 더 복잡합니다. (아래 예제는, 간단하게 8비트로 된 부호있는 정수형이지만, 모든 크기의 부호 있는 정수형에 적용되는 원칙은 같습니다 )

부호 있는 정수형은 첫번째 비트(부호비트(sign bit))를 이 정수형이 양수(positive)인지 음수(negative)인지 가리키기 위해 사용합니다. 부호 비트 0은 양수를 의미하고, 부호 비트 1은 음수를 의미합니다.

남아있는 비트들(값 비트(value bits))에 실제 값을 저장합니다. 양수는 부호 없는 정수형과 같은 방법으로 저장되며, 0부터 게산합니다.

다음은 숫자 4에 대한 Int8 비트 내부를 보여줍니다.

부호 비트가 0(양수를 의미)이고, 7개의 값 비트는 숫자 4이며, 2진수 표기법으로 작성되었습니다.

하지만 음수는, 다르게 저장됩니다. 2의 n제곱승(power) 에서 절대값을 빼서 저장되며, n은 값 비트 개수입니다. 8비트 숫자는 7개의 값 비트를 가지며, 2의 7 제곱승 또는 128을 의미합니다.

다음은 숫자 -4에 대한 Int8 비트 내부를 보여줍니다.

이번에는, 부호 비트가 1(음수를 의미)이고, 7개의 값 비트는 124의 2진 값을 가지고 있습니다 (128 - 4)

음수 숫자에 대한 인코딩은 2의 보수(two’s complement) 표현법이라고 합니다. 음수를 표현하는 것이 이상하게 보일수 있지만, 몇가지 장점이 잇습니다.

첫번째로, -4에 -1을 더할 수 있으며, 단순하게 모든 8개의 비트들(부호 비트를 포함해서)의 표준 이진 덧셈을 수행하고, 계산이 완료되면 8비트에 맞지 않는 것들을 버립니다.

두번째로, 2의 보수 표현법은 음수의 비트들을 왼쪽과 오른쪽으로 밀고(shift), 왼쪽으로 밀었을때 그 값이 여전히 두배가 되거나, 오른쪽으로 밀었을때에는(shift) 반절이 됩니다. 이를 위해서, 부호있는 정수를 오른쪽으로 밀때(shifted) 추가 규칙이 사용됩니다 : 부호있는 정수를 오른쪽으로 밀때(shift), 부호 없는 정수형과 같은 규칙이 적용되지만, 왼쪽에 비어있는 비트들을 0보다는 부호 비트(sign bit)로 채웁니다.

이 동작은 부호 있는 정수형이 오른쪽으로 밀린(shifted)뒤에도 같은 부호를 가지도록 보장해 주고 산술 시프트(arithmetic shift)라고 합니다.

양수와 음수가 저장되는 특별한 방법때문에, 그것들 중 하나를 오른쪽으로 밀어(shifting)서 이동하면 0에 가까워 집니다. 부호 비트를 유지하는 것은 미는중(shift)에도 값이 0에 가까워짐에 따라, 음수의 정수형이 음수로 남아있는 것을 의미합니다.

오버플로우 연산자(Overflow Operators)

정수형 상수나 변수에 가질수 없는 값을 삽입하려고 하면, Swift는 기본적으로 유효하지 않는 값을 만드는 것을 허용하기 보다는 오류를 발생시킵니다. 이 동작은 너무 크거나 작은 숫자로 작업할때 추가적인 안정성을 제공합니다.

예를 들어, Int16 정수형 타입은 -32768과 32767 사이의 모든 부호있는 정수를 가질수 있습니다.범위를 벗어난 숫자로 Int16 상수나 변수를 설정하려고 하면 오류가 발생합니다.

var potentialOverflow = Int16.max
// potentialOverflow equals 32767, which is the maximum value an Int16 can hold
potentialOverflow += 1
// this causes an error

너무 크거나 너무 작은 값을 가질때 오류 처리를 제공하는 것은 경계값 조건에 대해 코딩할때 훨씬 더 융통성이 있습니다.

하지만, 사용가능한 비트를 줄이기 위해 오버플로우 조건이 필요할때, 오류를 발생시키는 대신에 이 동작을 선택할 수 있습니다. Swift는 정수형 계산에 대한 오버플로우 동작을 선택하는 3가지 산술 오버플로우 연산자(overflow operators)를 제공합니다. 이러한 연산자들은 언제나. &으로 시작합니다.

  • 오버플로우 더하기 (&+)
  • 오버플로우 빼기 (&-)
  • 오버플로우 곱하기 (&*)

값 오버플로우(Value Overflow)

숫자들은 음수와 양수 방향 모두에서 오버플로우할 수 있습니다.

다음은 부호없는 정수형이 양수 방향으로 오버플로우가 허용됬을때 발생하는 예제이며, 오버플로우 더하기 연산자(&+)를 사용합니다.

var unsignedOverflow = UInt8.max
// unsignedOverflow equals 255, which is the maximum value a UInt8 can hold
unsignedOverflow = unsignedOverflow &+ 1
// unsignedOverflow is now equal to 0

변수 unsignedOverflow는 UInt8이 가질수 있는 최대 값(255 또는 이진수 11111111)으로 초기화 됩니다. 그리고나서 오버플로우 더하기 연산자(&+)를 사용해서 1씩 증가시킵니다. 이것은 UInt8이 가질수 있는 크기 이상의 이진(binary) 표현을 밀어넣어(pushes), 아래 그림에서 보는 것처럼, 범위를 넘어서 오버플로우가 발생합니다. 오버플로우 더하기 하고 난 후에 UInt8범위의 남은 값은 이진수 00000000 또는 0입니다.

부호 없는 정수형이 음수 방향으로 오버플로우가 허용될때에도 비슷합니다. 다음은 오버플로우 빼기 연산자(&-)를 사용한 예제 입니다.

var unsignedOverflow = UInt8.min
// unsignedOverflow equals 0, which is the minimum value a UInt8 can hold
unsignedOverflow = unsignedOverflow &- 1
// unsignedOverflow is now equal to 255

UInt8이 가질수 있는 최소 값은 0 또는 이진수 00000000입니다. 오버플로우 빼기 연산자(&-)를 사용해서 00000000에서 1을 빼는 경우에, 숫자는 오버플로우 되고 11111111 또는 10진수 255가 될 것입니다.

또한, 오버플로우는 부호 있는 정수형에서도 발생합니다. 비트단위 왼쪽과 오른쪽 시프트 연산(Bitwise Left and Right Shift Operators)에서 설명된 것 처럼, 부호 있는 정수형에 대한 모든 더하기와 빼기는 더하거나 빼는 숫자와 부호비트를 포함해서 비트단위로 수행됩니다.

var signedOverflow = Int8.min
// signedOverflow equals -128, which is the minimum value an Int8 can hold
signedOverflow = signedOverflow &- 1
// signedOverflow is now equal to 127

Int8이 가질수 잇는 최소 값은 -128 또는 이진수 10000000입니다. 오버플로우 연산자로 이진수에서 1을 뺀 이진 값은 01111111이며, 부호 비트와 양수 127을 토글(toggles)시키며, Int8이 가질수 있는 최대 양수값입니다.

부호가 있고 부호가 없는 정수형 모두, 양수 방향으로 오버플로우가 발생하면 정수의 유효한 최대 값에서 최소 값까지 래핑(wraps)되고, 수 방향으로 오버플로우가 발생하면 최소 값에서 최대값까지 랩핑합니다.

우선순위와 연관성(Precedence and Associativity)

연산자 우선순위(precedence)는 다른 연산자보다 더 높은 우선순위를 줍니다. 이러한 연산자 먼저 적용됩니다.

연산자 연관성(associativity)은 같은 우선순위의 연산자들을 함께 그룹화하는 방법을 정의합니다 - 왼쪽에서 부터 그룹화되거나, 오른쪽으로 부터 그룹화. 왼쪽에 있는 표현식과 연관있다 또는 오른쪽에 있는 표현식과 연관있다라는 의미로 생각합니다.

복합 표현식이 계산될 순서를 결정할때, 각각 연산자의 우선순위와 연관성을 고려하는 것은 중요합니다. 예를 들어, 연산자 우선순위는 다음 표현식이 왜 17이 되는지 설명합니다.

2 + 3 % 4 * 5
// this equals 17

왼쪽에서 오른쪽으로 정확히 읽으면, 다음과 같이 계산된 표현식을 예상할 수 있습니다.

  • 2 더하기 3은 5
  • 5 나머지 4는 1
  • 1 곱하기. 5는 5

하지만, 실제 답은 5가 아니라 17입니다. 높은 우선순위 연산자가 낮은 우선순위 전에 처리됩니다. Swift에서는 C 처럼, 나머지 연산자(%)와 곱하기 연산자(*)는 더하기 연산자(+)보다 더 높은 우선순위를 가집니다. 결과적으로, 더하기를 구하기 전에 둘다 처리 됩니다.

하지만, 나머지(remainder)와 곱하기(multiplication)는 각기 서로 같은(same) 우선순위를 가집니다. 정확한 순서로 처리하려면, 연관성(associativity)을 고려해야 합니다. 나머지와 곱하기 모두 왼쪽에 있는 표현과 연관(associate)되어 있습니다. 왼쪽에서 시작하는, 이러한 표현식에 암시적으로 괄호(())가 추가 된 것으로 생각합니다.

2 + ((3 % 4) * 5)

(3 % 4)는 3이며, 다음과 같습니다.

2 + (3 * 5)

(3 * 5)는 15이며, 다음과 같습니다.

2 + 15

이 계산은 마지박 값 17을 산출(yields)합니다.

Swift 표준 라이브러리에서 제공된 연산자에 대한 자세한 정보는, 연산자 우선순위 그룹의 전체 목록과 연관성 설정은, 연산자 선언하기(Operator Declarations)를 보세요.

주의
Swift의 연산자 우선순위와 연과성 규칙은 C와 Objective-C 보다 간단하고 더 예측 가능합니다. 하지만 , C 기반의 언어와 정확히 같지 않다는 것을 의미합니다. 기존 코드를 Swift로 이식(porting)할때 의도한 방식대로 연산자 동작이 작동하도록 주의해야 합니다.

연산자 메소드(Operator Methods)

클래스와 구조체는 기존 연산자를 자체적으로 구현할 수 있습니다. 이것은 기존 연산자를 오버로딩(overloading)한다라고 합니다.

아래 에제는 사용자정의 구조체에 대해 산술 더하기 연산자(+) 구현하는 방법을 보여줍니다. 산술 더하기 연산자는 2개의 대상에 대해서 동작하기 때문에, 2항 연산자(binary operator)이고 2개의 대상 사이에 나타나기 때문에, 중위(infix)라고 합니다.

예제는 2차원 위치 벡터(x, y)에 대한Vector2D 구조체를 정의하며, 뒤이어 Vector2D 구조체 인스턴스를 더하는 연산자 메소드(operator method)를 정의합니다.

struct Vector2D {
    var x = 0.0, y = 0.0
}

extension Vector2D {
    static func + (left: Vector2D, right: Vector2D) -> Vector2D {
        return Vector2D(x: left.x + right.x, y: left.y + right.y)
    }
}

연산자 메소드는 Vector2D에서 오버로드될 연산자와 일치하는 메소드 이름(+)으로, 타입 메소드처럼 정의됩니다. 더하기는 벡터에 대한 필수 동작이 아니기 때문에, 타입 메소드는 Vector2D의 메인 구조체 선언 보다는 Vector2D의 확장에서 정의됩니다. 산술 더하기 연산자는 2항 연산자 이기 때문에, 이 연산자 메소드는 Vector2D 타입의 입력 매개변수 2개를 받고 Vector2D타입의 출력 값 하나를 반환 합니다.

이 구현에서, Vector2D 인스턴스를+연산자의 왼쪽과 오른쪽을 표현하기 위해 left와 right이름으로된 입력 매개변수를 가집니다. 이 메소드는 새로운 Vector2D 인스턴스를 반환하며, x와 y 프로퍼티는 함께 추가된 2개의 Vector2D 인스턴스의 x와 y프로퍼티의 합으로 초기화 됩니다.

타입 메소드는 기존 Vector2D 인스턴스간에 중위(infix) 연산자 처럼 사용될 수 있습니다.

let vector = Vector2D(x: 3.0, y: 1.0)
let anotherVector = Vector2D(x: 2.0, y: 4.0)
let combinedVector = vector + anotherVector
// combinedVector is a Vector2D instance with values of (5.0, 5.0)

이 예제는 아래 그림처럼, (5.0, 5.0)벡터를 만들기 위해 (3.0, 1.0)과 (2.0, 4.0) 벡터들을 함께 더합니다.

전위와 후위 연산자(Prefix and Postfix Operators)

위 예제는 2항 중위 연산자(binary infix operator)의 사용자정의 구현한 것을 보여줍니다. 클래스와 구조체는 표준 단항 연산자(unary operators)구현을 제공할 수 있습니다. 단항 연산자(Unary operators)는 단일 대상에서 동작합니다. 그것은 대상 앞에 있는 것은(-a전위(prefix)이고 대상의 뒤에 있는 것은(b!후위(postfix) 연산자 입니다.

연산자 메소드를 선언할때 func키워드 앞에 prefix나 postfix 수식어를 작성해서 전위(prefix)나 후위(postfix) 단항 연산자(unary operator)를 구현합니다.

extension Vector2D {
    static prefix func - (vector: Vector2D) -> Vector2D {
        return Vector2D(x: -vector.x, y: -vector.y)
    }
}

위 예제는 Vector2D인스턴스에 대한 단항 빼기 연산자(-a)를 구현합니다. 단항 빼기 연산자는 전위(prefix) 연산자이고, 이 메소드는 prefix 수식어를 사용할 자격이(qualified)있습니다.

간단한 숫자 값의 경우, 단항 빼기 연산자는 양수를 음수로 변환하고 반대의 경우도 마찬가지입니다. Vector2D 인스턴스에 해당하는 구현은 x와 y프로퍼티 모두에서 연산을 수행합니다.

let positive = Vector2D(x: 3.0, y: 4.0)
let negative = -positive
// negative is a Vector2D instance with values of (-3.0, -4.0)
let alsoPositive = -negative
// alsoPositive is a Vector2D instance with values of (3.0, 4.0)

복합 할당 연산자(Compound Assignment Operators)

복합 할당 연산자(Compound assignment operators)는 할당(=)과 다른 연산자를 결합합니다. 예를 들어, 더하기 할당 연산자(+=)는 더하기와 할당하는 것을 하나의 동작으로 결합합니다. 매개변수 값들이 연산자 메소드 안에서 수정되야 하기 때문에, 복합 할당 연산자의 왼쪽 입력 매개변수의 타입은 inout으로 표시합니다.

아래 예제는 Vector2D인스턴스에 대해 더하기 할당 연산자 메소드를 구현한 것입니다.

extension Vector2D {
    static func += (left: inout Vector2D, right: Vector2D) {
        left = left + right
    }
}

더하기 연산자는 나중에 정의되기 때문에, 여기에서 더하기 과정을 재구현할 필요는 없습니다. 더하기 할당 연산자 메소드는 기존 더하기 연산자 메소드들 이용하고, 왼쪽 값과 오른쪽 값을 더해서 왼쪽 값을 설정하는데 사용합니다.

var original = Vector2D(x: 1.0, y: 2.0)
let vectorToAdd = Vector2D(x: 3.0, y: 4.0)
original += vectorToAdd
// original now has values of (4.0, 6.0)

주의
기본 할당 연산자(=)를 오버로드(overload)하는 것은 불가능합니다. 복합 할당 연산자만 오버로드(overloaded)할 수 있습니다. 3항 복합 연산자 (a ? b : c)는 오버로드 할 수 없습니다.

등가 연산자(Equivalence Operators)

기본적으로, 사용자정의 클래스와 구조체는 등가 연산자(equivalence operators)의 기본 동작을 사용하지 못하며, 같음(equal to) 연산자(==)와 같지 않음(not equal to) 연산자(!=)로 알려져 있습니다.

사용자정의 타입이 같은지(equivalence) 검사히기 위해 등가 연산자(equivalence operators) 를 사용하며, 다른 중위 연산자와 같은 방법으로 같음(equal to) 연산자의 구현을 제공하고, 표준 라이브러리의 Equatable 프로토콜을 준수하도록 추가합니다.

extension Vector2D: Equatable {
    static func == (left: Vector2D, right: Vector2D) -> Bool {
        return (left.x == right.x) && (left.y == right.y)
    }
}

위 예제는 2개의 Vector2D인스턴스가 같은 값을 가지는지 검사하기 위한 같음(equal to) 연산자 (==)를 구현합니다. Vector2D의 문맥상(context), 인스턴스 모두 같은 x값과 y값을 가지고 있다를 의미하는 같음(equal)이라고 생각하고, 이 로직은 연산자 구현에 의해 사용됩니다. 같음(equal to) 연산자를 구현하는 경우, 일반적으로 같지 않음(not equal to) 연산자(!=) 구현은 필요하지 않습니다. 표준 라이브러리는 구현한 같음(equal to) 연산자의 결과를 단순히 반대하는(negates), 같지 않음(not equal to) 연산자의 기본 구현을 제공합니다.

이제, 2개의 Vector2D 인스턴스가 같은지 검사하기 위해 이러한 연산자들을 사용할 수 있습니다.

let twoThree = Vector2D(x: 2.0, y: 3.0)
let anotherTwoThree = Vector2D(x: 2.0, y: 3.0)
if twoThree == anotherTwoThree {
    print("These two vectors are equivalent.")
}
// Prints "These two vectors are equivalent."

많은 간단한 경우에, Swft에게 등가 연산자의 합성된(synthesized) 구현을 제공하도록 요청할 수 있습니다. Swift는 다음에 오는 사용자정의 타입 종류에 대해 합성된 구현을 제공합니다.

  • Equatable 프로토콜을 준수하는 저장 프로퍼티만 가지는 구조체
  • Equatable 프로토콜을 준수하는 연관된 타입만 가지는 열거형
  • 연관된 타입이 없는 열거형

이러한 구현을 사용하기위해(receives) 원래 선언이 포함된 파일에서 Equatable 적합성(conformance) 선언합니다.

아래 예제는 3차원(three-dimensional) 위치 벡터(x, y, z)에 대한 구조체 Vector3D 정의하며, Vector2D 구조체와 비슷합니다. x, y, z 프로퍼티는 모두 Equatable 타입이기 때문에, Vector3D는 등가 연산자의 기본 구현을 사용합니다.

struct Vector3D: Equatable {
    var x = 0.0, y = 0.0, z = 0.0
}

let twoThreeFour = Vector3D(x: 2.0, y: 3.0, z: 4.0)
let anotherTwoThreeFour = Vector3D(x: 2.0, y: 3.0, z: 4.0)
if twoThreeFour == anotherTwoThreeFour {
    print("These two vectors are also equivalent.")
}
// Prints "These two vectors are also equivalent."

사용자정의 연산자(Custom Operators)

Swift에서 제공하는 표준 연산자외에도 사용자정의 연산자(custom operators)를 선언하고 구현할 수 있습니다. 사용자정의 연산자를 정의하기 위해 사용될수 있는 특징들에 대한 목록은 연산자(Operators)를 보세요.

새로운 연산자는 operator키워드를 사용해서 전역 레벨에서 선언되고, prefix, infix, postfix 수식어로 표시됩니다.

prefix operator +++

위 예제는 새로운 전위 연산자(prefix operator) +++를 정의합니다. 이 연산자는 Swift에 존재하지 않고, Vector2D인스턴스로 작업하는 특정 상황(context)에서 자체적인 사용자정의 의미를 부여 받습니다. 이 예제의 목적을 위해, +++는 새로운 prefix doubling 연산자처럼 처리됩니다. 이전에 정의된 더하기 할당연산자로 그 자체에 벡터를 대해서, Vector2D인스턴스의 x와 y값을 2배로 만듭니다. +++ 연산자 구현을 위해, 다음과 같이 Vector2D에 타입 메소드 +++를 추가합니다.

extension Vector2D {
    static prefix func +++ (vector: inout Vector2D) -> Vector2D {
        vector += vector
        return vector
    }
}

var toBeDoubled = Vector2D(x: 1.0, y: 4.0)
let afterDoubling = +++toBeDoubled
// toBeDoubled now has values of (2.0, 8.0)
// afterDoubling also has values of (2.0, 8.0)

사용자정의 중위 연산자에 대한 우선순위(Precedence for Custom Infix Operators)

사용자정의 중위 연산자(custom infix operators)들은 각각 우선순위 그룹에 속해있습니다. 우선순위 그룹은 연산자의 연관성(associativity) 뿐만아리라, 다른 infix 연산자와 관련하여 우선순위를 지정합니다. 이러한 특성들이 중위 연산자와 다른 중위 연산자와 영향을 주는지에 대한 설명은 우선순위와 연관성(Precedence and Associativity)를 보세요.

우선순위 그룹에 명시적으로 배치하지 않은 사용자정의 중위(infix)연산자는 3항 조건 연산자(ternary conditional operator)의 우선순위보다 더 높은, 기본 우선순위 그룹이 지정됩니다.

아래 예제는 우선순위 그룹 AdditionPrecedence에 속하는 새로운 중위(infix) 연산자. +-를 정의합니다.

infix operator +-: AdditionPrecedence
extension Vector2D {
    static func +- (left: Vector2D, right: Vector2D) -> Vector2D {
        return Vector2D(x: left.x + right.x, y: left.y - right.y)
    }
}
let firstVector = Vector2D(x: 1.0, y: 2.0)
let secondVector = Vector2D(x: 3.0, y: 4.0)
let plusMinusVector = firstVector +- secondVector
// plusMinusVector is a Vector2D instance with values of (4.0, -2.0)

이 연산자는 두 벡터의 x 값을 더하고, 첫번째 벡터에서 두번째 벡터의 y값을 빼줍니다. 이것은 본질적으로 더하는(additive)연산자 이기때문에, +와 -처럼, 더하기 중위 연산자(additive infix operators)와 같은 우선순위 그룹이 주어집니다. Swift 표준 라이브러리에 의해 제공되는 연산자에 대한 정보, 연산자 우선순위 그룹과 연관성(associativity) 설정의 전체 목록을 보려면, 연산자 선언(Operator Declarations)를 보세요. 우선순위 그룹에 대한 자세한 내용과 자신만의 연산자와 우선순위 그룹을 정의하는 문법을 보려면, 연산자 선언(Operator Declaration)을 보세요.

주의
전위(prefix)나 후위(postfix) 연산자를 정의할때는 우선순위를 지정하지 않습니다. 하지만, 전위(prefix)와 후위(postfix) 연산자 모두 동일한 피연산자(operand)인 경우에, 후위(postfix) 연산자가 먼저 적용됩니다.


반응형

'Swift > Language Guide' 카테고리의 다른 글

불분명한 타입(Opaque Types)  (0) 2019.12.12
컬렉션 타입(Collection Types)  (0) 2019.03.05
문자열과 문자(Strings and Characters)  (0) 2019.02.28
기본 연산자(Basic Operators)  (0) 2019.02.26
Access Control  (0) 2018.09.18
Memory Safety  (0) 2018.09.18
Automatic Reference Counting  (0) 2018.09.18
Generics  (0) 2018.09.18
Posted by 까칠코더
,