[Swift] Advanced Operators
Advanced Operators
정수 타입 / 숫자 리터럴
스위프트 정수 타입과 범위
- 플랫폼 사양에 따르는 타입 : Int, UInt (최근 대부분 64비트)
- 8-bit : Int8, UInt8
- 16-bit : Int16, UInt16
- 32-bit : Int32, UInt32
- 64-bit : Int64, UInt64
MemoryLayout<Int8>.size // 1바이트
Int8.max // 127 ( 2^7 -1)
Int8.min // -128 (-2^7)
MemoryLayout<UInt8>.size // 1바이트
UInt8.max // 255 ( 2^8 -1)
UInt8.min // 0
MemoryLayout<Int>.size // 8바이트
// 64비트 컴퓨터 기준
// 63 = 2^8 - 1 (부호 비트)
Int.max // 9223372036854775807 ( 2^63 -1)
Int.min // -9223372036854775808 (-2^63 )
오버플로우
- C언어나 Objective-C언어의 산술연산자에서는 값이 넘침(overflow)을 허락했음
(예를 들어, 8비트 값을 담을 수 있는 숫자에서 255를 넘어가면 다시 0으로 순환) - 스위프트에서는 오버플로우를 기본적으로 허락하지 않음
-> 에러발생 (크래시) - 오버플로우의 방향은 양(positive)의 방향, 음(nagative)의 방향을 모두 의미
- 특정한 경우에, 특정패턴을 구현하기 위해 오버플로우를 허용하는 경우가 필요한데, 이런 경우 활용을 위해 오버플로우 연산자를 마련해 놓았음
오버플로우 연산자
- 이항연산자: 연산자를 중심으로 왼쪽과 오른쪽에 표현식이 위치
(스위프트에서 3가지 오버플로우 연산자 제공)
연산자 기호 | 프로그래밍 용어 | 설명 |
---|---|---|
&+ | 오버플로우 더하기 연산자 (Overflow Addtion Operator) | 상위 범위에서 벗어나면 순환해서 가장 작은 수로 이동 |
&- | 오버플로우 빼기 연산자 (Overflow Subtraction Operator) | 하위 범위에서 벗어나면 순환해서 가장 큰 수로 이동 |
&* | 오버플로우 곱하기 연산자 (Overflow Multiplication Operator) | 곱해서 범위에서 벗어나는 것 허용 |
논리연산자와 단락 평가
- 논리 연산자
- 이항연산자 연산자를 중심으로 왼쪽과 오른쪽에 Boolean 표현식이 위치해야함. 결과값 또한 항상 참 또는 거짓으로 도출
단락 평가
- 스위프트의 논리 평가식은 단락 평가 방식을 사용
- 단락평가: 논리 평가식에서 결과도출에 필요한 최소한의 논리식만 평가
- 참을 찾을때 까지만 실행하고, 참을 찾으면 나머지 표현식은 평가하지 않음
- 최소한의 코드만 실행 - Short-circuit evaluation
- 논리연산자 우선순위
&&
||
단락 평가 시 유의 사항
논리평가식 내에 선언된 표현식의 평가에서 함수(메서드)의 실행이 일어날 수 있고, 사이드 이펙트(Side-Effect)를 발생시키는 경우, 단락평가로 인해 함수 등의 실행횟수의 차이로 인해 의도치 않은 결과가 도출될 수 있음을 주의
-> 논리적인 오류가 없도록 표현식을 미리 실행하도록 코드 수정
- 사이드 이펙트(Side-Effect)
- 함수 내부에서 일부 외부 변수의 값을 변경 시키는 것
- ex) num += 1
var doorCheck = 0
var passwordCheck = 0
func doorCodeChecking() -> Bool {
doorCheck += 1
print(#function)
return true
}
func passwordCodeChecking() -> Bool {
passwordCheck += 1
print(#function)
return true
}
// ---------- 첫번째 케이스 ----------
if doorCodeChecking() && passwordCodeChecking() && false || true && doorCodeChecking() && passwordCodeChecking() {
// 조건문에 대한 코드
}
print("Door: \(doorCheck), Password: \(passwordCheck)")
//-> Door: 2, Password: 2
// ---------- 두번째 케이스 ----------
doorCheck = 0
passwordCheck = 0
if doorCodeChecking() && false && passwordCodeChecking() || doorCodeChecking() || passwordCodeChecking() {
// 조건문에 대한 코드
}
print("Door: \(doorCheck), Password: \(passwordCheck)")
// -> Door: 2, Password: 0
// ---------- 세번째 케이스 ----------
doorCheck = 0
passwordCheck = 0
if doorCodeChecking() || passwordCodeChecking() && doorCodeChecking() || false && passwordCodeChecking() {
// 조건문에 대한 코드
}
print("Door: \(doorCheck), Password: \(passwordCheck)")
// -> Door: 1, Password: 0
// -------- 수정된 세번째 케이스 --------
doorCheck = 0
passwordCheck = 0
let doorResult1 = doorCodeChecking()
let passwordResult1 = passwordCodeChecking()
let doorResult2 = doorCodeChecking()
let passwordResult2 = passwordCodeChecking()
if doorResult1 || passwordResult1 && doorResult1 || false && passwordResult2 {
// 조건문에 대한 코드
}
print("Door: \(doorCheck), Password: \(passwordCheck)")
// -> Door: 2, Password: 2
비트 연산자
비트 연산
메모리 비트 단위로 직접적인 논리연산을 하거나, 비트 단위 이동시에 사용하는 연산
주로, 어떤 하드웨어적인 처리(예, 장치 드라이버 생성)나 그래픽 프로그래밍과 임베디드 프로그래밍, 암호화처리, 게임 등 아주 한정적으로 쓰이는 이론적인 내용
(프로그래밍을 배우고 있다는 목적아래, 이론적으로 듣고 지나치면 됨)장점
- 연산속도가 빠름 (직접적으로 메모리의 실제 비트를 컨트롤)
- 짧은 코드로 복잡한 로직을 구현 가능한 경우가 있음
스위프트 비트연산자 종류
- 비트 논리 연산자
~
: Bitwise NOT Operator(비트와이즈 낫 연산자)- 단항연산자로 사용. 기존 메모리 비트를 반전 시킴 (0 ➡ 1, 1 ➡ 0 반전)
let a1: UInt8 = 0b0000_1111 // 15 let b1 = ~a1 // 0b1111_0000 // 240
- 단항연산자로 사용. 기존 메모리 비트를 반전 시킴 (0 ➡ 1, 1 ➡ 0 반전)
&
: Bitwise AND Operator(비트와이즈 앤드 연산자)- 두개의 메모리 비트 중 모두가 1이면 1을 반환 (true && true의 논리)
let a2: UInt8 = 0b1111_1100 // 252 let b2: UInt8 = 0b0011_1111 // 63 let c2 = a2 & b2 // 0b0011_1100 // 60
- 두개의 메모리 비트 중 모두가 1이면 1을 반환 (true && true의 논리)
|
: Bitwise OR Operator(비트와이즈 오어 연산자)- 두개의 메모리 비트 중 하나라도 1이면 1을 반환 (true || true의 논리)
let a3: UInt8 = 0b1011_0010 // 178 let b3: UInt8 = 0b0101_1110 // 94 let c3 = a3 | b3 // 0b1111_1110 // 254
- 두개의 메모리 비트 중 하나라도 1이면 1을 반환 (true || true의 논리)
^
: Bitwise XOR Operator(비트와이즈 엑스오어 연산자)- 두개의 메모리 비트 중 둘을 비교해서 서로 다르면 1을 반환 (서로 같으면 0반환)
let a4: UInt8 = 0b0001_0100 // 20 let b4: UInt8 = 0b0000_0101 // 5 let c4 = a4 ^ b4 // 0b0001_0001 // 17
- 두개의 메모리 비트 중 둘을 비교해서 서로 다르면 1을 반환 (서로 같으면 0반환)
- 비트 이동 연산자
<<
: Bitwise Left Shift Operator(비트와이즈 레프트 시프트 연산자)- 모든 비트를 원하는 값만큼 왼쪽으로 이동 (2를 곱하는 효과)
- 부호가 있건 없건 상관없음
let leftShiftBits: UInt8 = 4 // 0b0000_0100 // 4 leftShiftBits << 1 // 0b0000_1000 // 8 (곱하기 2) leftShiftBits << 2 // 0b0001_0000 // 16 (곱하기 2^2승 => 곱하기 4) leftShiftBits << 5 // 0b1000_0000 // 128 (곱하기 2^5승 => 곱하기 32)
>>
: Bitwise Right Shift Operator(비트와이즈 라이트 시프트 연산자)- 모든 비트를 원하는 값만큼 오른쪽으로 이동 (2를 나누는 효과)
- 부호가 있을때만 주의 필요
- 산술시프트 : 오른쪽으로 이동시에는 부호 유지를 위해, 부호가 있을땐 1 삽입됨 (부호가 없을땐 0 삽입)
let rightShiftBits: UInt8 = 32 // 0b0010_0000 // 32 rightShiftBits >> 1 // 0b0001_0000 // 16 (나누기 2) rightShiftBits >> 2 // 0b0000_1000 // 8 (나누기 4) rightShiftBits >> 5 // 0b0000_0001 // 1 (나누기 2^5승)
커스텀 연산자
사용자가 직접 정의해서(Custom) 연산자 메서드를 구현하는 방법
기본 연산자
// 1) 연산자 (위치) 선언
postfix operator <++ // 후치 연산자
prefix operator ++> // 전치 연산자
// 2) 연산자의 실제 정의
extension Int {
// 후치 연산자
static postfix func <++(number: inout Int) {
number += 1
}
// 전치 연산자
static prefix func ++>(number: inout Int) {
number += 1
}
}
// 커스텀 연산자의 사용
var a = 0
++>a // +1
a<++ // +1
print(a) // 2
중위 연산자
// 1) 우선순위 그룹의 선언 (우선순위, 결합성 설정)
precedencegroup MyPrecedence {
higherThan: AdditionPrecedence
lowerThan: MultiplicationPrecedence
associativity: left // 결합성 : left / right / none
}
// 2) (전역의 범위에서) 정의하려는 연산자를 선언하고, 우선순위 그룹을 지정
// 단항 -> 전치(prefix), 후치(postfix) / 이항 -> infix
// "우선 순위와 결합성"을 지정은 새로운 우선순위 그룹을 선언하거나, 이미 존재하는 우선 순위 그룹을 사용하는 것도 가능
// 우선순위 그룹을 지정하지 않으면 "DefaultPrecedence"라는 기본 그룹에 속하게 됨
// 삼항연산자보다 한단계 높은 우선순위가 되며, 결합성은 none설정되어 다른 연산자와 결합 사용은 불가능
infix operator +-: MyPrecedence
// 3) 연산자의 실제 정의
// 해당 연산자를 구현하려는 타입에서 타입메서드로 연산자 내용을 직접 구현
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
print(plusMinusVector) // Vector2D(x: 4.0, y: -2.0)