Merge changes from dyanos into master

FlashMaestro authored
revision beeae465a5375410fd5f9bafb84d32795ad4423c
chapter25
# 25 고급 연산자 (Advanced Operators)
> Translator : 이름 (메일주소심상진 (dyanos@gmail.com)

기본 연산자 항목에서 설명했던 연산자들에 더하여, Swift는 훨씬 다양한 방법으로 값을 다루는 몇개의 고급 연산자들을 제공합니다. 이들은 당신이 C와 Objective-C에서 부터 친근하게 여겼던 비트를 다루는 연산자들 모두를 포함합니다.

C에서의 산술 연산자들과는 다르게, Swift에서의 산술 연산자들은 기본적으로 넘침(Overflow)이 일어나지 않습니다. 넘치는 동작(Overflow behavior)은 오류로써 잡히고 보고됩니다. 넘치는 동작을 허용하기 위해서, 넘침을 기본으로 하는 산술 연산들 중에 Swift의 두번째 집합을 사용해야 합니다. 예를 들어, 넘침 덧셈(overflow addition, &+)이 그러한 집합에 속합니다. 모든 넘침 연산자들은 엠퍼샌드(ampersand, &)를 가지고 시작합니다.

당신이 당신 소유의 구조체들과 클래스, 그리고 열거자들을 선언할때, 이들 커스텀 타입들에 대해서 표준 Swift 연산자들의 독자적인 구현들(own implementations)을 제공하는데 유용할 수 있습니다. Swift는 이들 연산자들의 맞춤형(tailored) 구현들을 제공하고 그들의 행동이 당신이 만든 각각의 타입에 대해서 무엇을 해야할지를 정확하게 결정하기 쉽게 만듭니다. (역자 주, operator overload)

당신은 연산자들을 재정의하는데 아무런 제한이 없습니다. Swift는 당신에게 당신 자신의 맞춤형 중간(infix), 전위(prefix), 후위(postfix) 그리고 할당 연산자들을 정의하는데 자유를 줍니다. 그리고 그것들의 우선순위와 결합순위 역시 자유롭게 정의가 가능합니다. 이들 연산자들은 마치 이미 선언된 연산자들 처럼 당신의 코드 안에 사용되고 적용될 수 있고, 당신은 당신이 정의한 맞춤형 연산자들을 지원하도록 이미 존재하는 타입들조차 확장할 수 있습니다.

**비트 연산자들**

비트 연산자들은 당신에게 하나의 데이터 구조체 안에 있는 개개의 가공되지 않은 데이터 비트들(raw data bits)을 다루는 것을 허용합니다. 그들은 종종 그래픽 프로그래밍과 디바이스 드라이버 프로그래밍(device driver creation)과 같은 저수준 프로그래밍에 사용됩니다. 또한 비트 연산자들은 당신이 외부의 입력들(external source)로부터 오는 가공되지 않은 데이터(raw data)를 가지고 작업할때 유용합니다. 예를 들어, 커스텀 프로토콜을 이용한 통신에서 데이터의 부호화(encoding)와 복호화(decoding)과 같은 것들이 그것입니다.

Swift는 C에서 발견되는 모든 비트 연산자들을 지원합니다. 이는 아래에서 좀더 자세히 설명드리겠습니다.

**비트 NOT 연산자**

비트 NOT 연산자(~)는 다음과 같이 숫자의 모든 비트들을 뒤집습니다.(invert)



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

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

UInt8 정수들은 8개의 비트를 가지며, 0에서부터 255까지의 임의의 값을 저장할 수 있습니다. 이 예는 UInt8 정수 변수를, 최초의 4개 비트는 0으로, 나머지 4개비트는 1로 설정한, 이진 값 00001111을 가지도록 초기화합니다. 이것은 십진수 15와 동일한 것입니다.

다음 줄에서, 비트 NOT 연산자는 invertedBits라 불리우는 새로운 상수를 생성하는데 사용합니다. 이것은 initialBits와 동일하지만 모든 비트들이 뒤집어져 있습니다. 다시말해, 이때 initialBit의 비트들중에 0은 1이되고, 1은 0이 됩니다. "그러므로" invertedBits의 값은 11110000이 됩니다. 이것은 부호없는 십진수 240과 동일합니다.

**비트 AND 연산자**

비트 AND 연산자(&)는 두 숫자의 비트들을 결합합니다. 비트들이 다음과 같이 양쪽 입력 숫자들에서 1과 같아야만 비트들이 1로 설정되는 새로운 숫자을 돌려받습니다.(""""좀더 명확하게 이해되도록 수정해야할 필요가 있음"""")



아래의 예에서, firstSixBits변수와 lastSixBits양쪽의 값들은 4개의 중간 비트가 1로 되어있습니다. 비트 AND 연산자는 그들을 부호없는 십진수 60과 동일한 숫자인 00111100로 만들도록 조합합니다.

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

**비트 OR 연산자**

비트 OR 연산자(|)는 두 수의 비트들을 비교합니다. 연산자는 만일 다음처럼 입력 수들 중에 어떤 하나가 비트 1이면, 비트가 1로 설정된 새로운 수를 돌려줍니다.



아래의 예제에서, someBits와 moreBits의 값은 1로 설정된 다른 비트들을 가집니다. 비트 OR 연산자는 그들을 부호없는 십진수 256와 동일한 숫자인 11111110으로 만들어지도록 조합합니다.

1:let someBits: UInt8 = 0b10110010
2:let moreBits: UInt8 = 0b01011110
3:let combinedbits = someBits | moreBits // equals 11111110”

**비트 XOR 연산자**

비트 XOR 연산자 또는 배타적(exclusive) OR 연산자 (^)는 두 수의 비트들을 비교합니다. 연산자는 다음과 같이 입력 비트들이 다르면 1로 같으면 0으로 설정되어진 새로운 수를 돌려받습니다.



아래 예에서, firstBits와 otherBits 각각의 값들은 하나의 위치에서 1로 설정된 하지만 다른 변수에서는 그렇지 않은 비트를 가집니다. 비트 XOR 연산자는 그것들의 출력 값에서 이들 비트들의 양쪽을 1로 설정합니다. firstBits와 otherBits에서 모든 다른 비트들은 같으며, 이것은 다음과 같이 출력 값에서 0으로 나타납니다.

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

**비트 왼쪽 및 오른쪽 이동(shift) 연산자들**

비트 왼쪽 이동 연산자(<<)와 비트 오른쪽 이동 연산자(>>)는 수에서 아래 정의된 규칙에 따라서, 특정 수의 위치(a certain number of places)로 모든 비트들을 왼쪽 또는 오른쪽으로 이동시킵니다.

비트 왼쪽 그리고 오른쪽 이동은 2의 인수로 정수에 곱한 것과 나눈 것의 효과를 가집니다. 왼쪽으로 한 자리만큼 정수의 비트들을 이동하는 것은 값을 두배로 하는 것과 같은 효과를 나타냅니다. 마찬가지로 오른쪽으로 이동하는 것은 2로 나누는 것과 동일한 효과를 가집니다.

**부호없는 정수들에 대한 이동 방법**

부호없는 정수의 비트 이동은 다음처럼 합니다.

1. 존재하는 비트들은 요청된 수의 위치로(the requested number of places) 왼쪽 또는 오른쪽으로 이동되어 집니다.
2. 정수 공간의 크기를 넘어 이동된 비트들은 버려집니다.
3. 원래의 비트들이 이동되고 남은 자리에 0이 삽입됩니다.

이 접근은 논리적 이동(또는 옮김)으로써 알려져 있습니다.

아래의 그림은 11111111<<1의 결과를 보여줍니다.(여기서는 왼쪽으로 1만큼 이동하는 것을 말합니다.) 그리고 11111111>>1(이것은 오른쪽으로 1만큼 이동하는 것을 말합니다.) 여기서 파란색 비트들은 이동된 비트들을 말하며, 회색 비트들은 버려진 것을 말합니다. 그리고 오랜지색의 0은 삽입된 것을 말합니다.

<>

여기서는 Swift 코드 안에서 어떻게 비트 이동을 하는지를 다음의 실제 코드로 보여줍니다.

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

당신은 다음과 같이 다른 데이터 타입들안에 있는 값들을 부호화하기 위해서 그리고 복호화하기 위해서 비트 이동을 사용할 수 있습니다.

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

이 예제는 핑크색에 대한 Cascading Style Sheets 색 값을 저장하기 위해 pink로 불리우는 UInt32 타입의 상수를 사용합니다. CSS 컬러 값 #CC6699는 Swift의 16진수 표현으로 0xCC6699가 됩니다. 이 색깔은 비트 AND 연산자(&)와 비트 오른쪽 이동 연산자(>>)를 사용하여 빨간색 (CC), 녹색(66), 파란색 (99) 요소들로 나누어서 나타낼 수 있습니다.

빨간색 요소는 숫자 0xCC6699와 0xFF0000사이에 비트 AND 연산을 수행하므로써 얻어집니다. 0xFF0000에서, 6699를 무시되게 하기 위해서 그리고 결과에서 0xCC0000를 남기기 위해서, 0은 효과적으로 0xCC6699의 두번째와 세번째 바이트를 가려줍니다.(mask)

그때 이 수는 오른쪽으로 16칸 이동(>>16)합니다. 16진수에서의 두자리는 2진수의 8비트와 같습니다, 그래서 오른쪽으로 16칸짜리 이동은 0xCC0000를 0x0000CC로 변환할 것 입니다. 이것은 10진수 204인 0xCC와 같습니다.

비슷하게, 녹색 요소는 출력으로써 0x006600을 주는 0xCC6699와 0x00FF00사이에 비트 AND 연산을 수행하므로써 얻어집니다. 이 출력은 오른쪽으로 8칸 이동되어 지고, 10진수로 102에 해당하는 0x66의 값을 줍니다.

마지막으로, 파란색 요소는 출력으로 0x000099를 주는 0xCC6699와 0x0000FF사이의 비트 AND 연산을 수행하므로써 얻어집니다. 여기서는 오른쪽으로의 이동이 필요없습니다. 이미 0x000099는 10진수로 153에 해당하는 0x99와 동일하기 때문입니다.

부호있는 정수의 이동 모습(behavior)

이동은 부호있는 정수를 할때 부호없는 정수때보다 더 복잡합니다. 이는 부호있는 정수를 이진수로 표현하는 방식때문입니다. (아래 예들은 간단함을 위해 8비트 부호있는 정수들을 기본으로 하여 진행됩니다. 그러나 어떠한 크기의 부호있는 정수에도 앞으로 나올 원칙을 적용할 수 있습니다.)

부호있는 정수들은 (부호 비트로 알려진) 그들의 첫번째 비트를 그 정수가 양의 정수인지 음의 정수인지를 타나내는데 사용합니다. 부호비트가 0이면 양수를, 부호비트가 1이면 음수를 의미합니다.

값 비트로 알려진 남아 있는 비트들은 실제 값을 저장합니다. 양의 정수는 정확하게 부호없는 정수에 대해서 하는 것과 같이 정확하게 같은 방법인 0부터 위쪽으로 계산하는 방법(counting upwards from 0)으로 저장합니다. 여기서는 어떻게 Int8안에서 숫자 4를 표현하는지 보여줍니다.



부호 비트가 0(즉, 양수)이고, 7개의 값 비트들은 단지 이진 표현으로 쓰여진 숫자 4를 의미합니다.

그렇지만 음수는 다르게 저장됩니다. 2의 n승에서 그들의 절대값을 빼므로써 저장됩니다. 이때 n은 값 비트의 수를 의미합니다. 8비트 수는 7개의 값 비트를 가집니다. 그래서 이것은 2의 7승 또는 128을 의미합니다.

여기서는 어떻게 Int8에서 -4를 표현하는지 보여줍니다.



이번에는, 부호 비트가 1(즉, 음수)이고, 7개의 비트는 이진값으로 (128 - 4인) 124를 가집니다.



음수에 대한 부호화 방법은 2의 보수 표현현으로써 알려져 있습니다. 이것은 이상한 방법처럼 보이지만, 이러한 방법은 몇가지 이득을 가집니다.

첫번째 당신은 다음과 같이 (부호 비트를 포함하는) 모든 8개의 비트들에 대해서 표준 이진 덧셈을 하고, 8비트에 적합하지 않은 어떤것도 버릴필요없이 간단하게 -1을 -4에 더할 수 있습니다.



두번째, 2의 보수 표현은 당신에게 양수에서와 같이 음수의 비트들을 왼쪽 또는 오른쪽으로 이동시키고, 여전히 왼쪽 이동에 대해서 그들을 배가하거나 오른쪽 이동으로 반분되어지도록 합니다. 이것을 이루기 위해서, 부호있는 정수를 오른쪽으로 이동시킬때 다음의 추가적인 규칙들이 적용됩니다.

당신이 오른쪽으로 부호있는 정수를 이동시킬때, 부호없는 정수에서와 같은 규칙들을 적용하면 됩니다만 부호와 함께 왼쪽에 있는 임의의 빈 비트들을 0과는 다른 것으로 채워야 합니다.



이러한 행동은 부호있는 정수들이 오른쪽으로 이동후에도 같은 부호를 가지는 것을 확실히 하기 위해서 입니다. 그리고 이러한 행동은 산술 이동이라고 알려져 있습니다.

양수와 음수가 저장되는 특별한 방식 때문에, 그들 중에 하나를 오른쪽으로 이동하는 것은 그들을 0으로 이동합니다. 이러한 이동동안 부호 비트를 같게 유지하는 것은 음수를 그들의 값을 0으로 옮기는 동안에도 음수로 남아있게 한다는 것을 의미합니다.

넘침 연산자들

만일 당신이 값을 유지할 수 없는 정수 상수 또는 변수에 하나의 숫자를 넣기를 시도한다면, 기본적으로 Swift는 유효하지 않은 값이 생성되기를 허락하기 보다는 오류를 보고 합니다. 이 행동은 당신이 너무 큰거나 너무 작은 숫자들을 가지고 작업할때 추가적인 안전함(extra safety)을 당신에게 제공합니다.

예를 들어, Int16 정수 타입은 -32768부터 32767까지의 임의의 부호있는 정수를 가지고 있을 수 있습니다. UInt16 상수 또는 변수에 이 범위를 벗어나는 숫를 설정하려고 노력하는 것은 오류를 일으킵니다.

1:“var potentialOverflow = Int16.max
2:// potentialOverflow equals 32767, which is the largest value an Int16 can hold
3:potentialOverflow += 1
4:// this causes an error”

값이 너무 크거나 너무 작을때 다루는 오류를 제공하는 것은 경계값 조건에 대해서 코딩할때 훨씬 더 많은 유연성을 당신에게 줍니다.

그렇지만, 당신이 가능한 비트들의 수를 일부를 줄이기 위해서 넘침 조건을 특별히 원할때, 당신은 오류를 일으키는 것보다 다음의 행동으로 이를 수행할 수 있습니다. Swift는 정수 계산에 대해서 넘침 동작을 수행할 수 있는 다섯가지의 넘침 연산자들을 제공합니다. 이들 연산자들 모두는 앰퍼센트(&)를 가지고 시작합니다.

Overflow addition (&+)
Overflow subtraction (&-)
Overflow multiplication (&*)
Overflow division (&/)
Overflow remainder (&%)”

값 넘침

여기서는 넘침 덧셈 연산자(&+)를 사용하여, 부호없는 값이 넘침이 허용될때 무슨일이 일어나는지에 대한 예를 보여줍니다.

1:“var willOverflow = UInt8.max
2:// willOverflow equals 255, which is the largest value a UInt8 can hold
3:willOverflow = willOverflow &+ 1
4:// willOverflow is now equal to 0

변수 willOverflow는 UInt8이 가질 수 있는 최대 값(즉, 255 또는 이진수로 11111111)으로 초기화되어 있습니다. 그때 넘침 덧셈 연산자(&+)를 사용하여 1을 증가시킵니다. 이것은 그것들의 이진 표현을 UInt8의 크기를 넘도록 밀어내는데, 이것은 아래 그림에서 보여지듯이 범위를 넘어서서 넘침을 발생시킵니다. 넘침 덧셈 이후로 UInt8의 범위안에 남아있는 값은 00000000 또는 0입니다.



값 언더플로

숫자들은 또한 그들 타입의 최대 범위안에서 맞기에는 너무 작게 될 수 도 있습니다. 여기에 예제가 있습니다.

UInt8가 유지할 수 있는 가장 작은 수는 0(즉, 8비트 이진 형태에서는 00000000이 됩니다.)입니다. 만일 당신이 넘침 뺄셈 연산자를 사용하여 00000000으로부터 1을 뺀다면, 그 수는 이진수 11111111 또는 십진수 255으로 꺼꾸로 넘칠 것 입니다.



다음은 Swift코드 에서 어떻게 보이는 지를 나타냅니다.

1:var willUnderflow = UInt8.min
2:// willUnderflow는 UInt8이 유지할 수 있는 가장 작은 값인 0이 됩니다.
3:willUnderflow = willUnderflow &- 1
4:// 현재 willUnderflow는 255와 동일합니다.

유사한 언터플로는 부호있는 정수에서 발생됩니다. 부호있는 정수들에 대한 모든 뺄셈은 직접적인 이진 뺄셈으로써 수행됩니다. 이는 밸셈을 하고있는 숫자의 부분으로써 포함되어 있는 부호비트도 함께이며, 비트 왼쪽 그리고 오른쪽 연산자들에서 설명한 것과 같습니다. Int8인 가질 수 있는 가장 작은 값은 -128입니다. -128은 이진수로 10000000로 나타납니다. 넘침 연산자를 가지고 이 이진 수로부터 1을 빼는 것은 01111111의 이진 수를 줍니다. 이것은 부호비트를 뒤집고 양수 127을 줍니다. 이는 Int8이 가질 수 있는 가장 큰 양의 수입니다.



다음은 Swift코드에서의 표현입니다.

1:var signedUnderflow = Int8.min
2:// signedUnderflow는 -128과 같습니다. 이는 Int8이 가질 수 있는 가장 작은 값입니다.
3:signedUnderflow = signedUnderflow &- 1
4:// signedUnderflow는 지금 127과 같습니다.

위에 설명된 넘침과 언터플로의 행동의 마지막 결과는 부호있는 그리고 부호없는 정수 양쪽에 대해서, 항상 넘침이 가장 크게 유효한 정수 값으로 부터 가장 작은 것으로 반복되며, 언더플로는 가장 작은 값으로부터 가장 큰 값으로 반복됩니다.

0으로 나누기

0으로 숫자를 나는 것(i/0) 또는 0으로 나머지를 계산하기(i%0)를 시도하는 것은 오류를 발생시킵니다.

1:let x = 1
2:let y = x / 0