[CH5.1] 연산자

Tabber·2021년 9월 23일
0
post-thumbnail

연산자(Operator)

스위프트의 연산자는 특정한 문자로 표현한 함수라고 할 수 있다.
따라서, 특정 연산자의 역할을 프로그래머의 의도대로 변경할 수도 있다.

연산자에 의해 연산되는 값의 수에 따라 단항, 이항, 삼항 등으로 구분하기도 하며, 연산자의 위치에 따라 전위, 중위, 후위 등으로 구분하기도 한다.

연산자의 분류

NOTE 띄어쓰기와 연산자
스위프트에서는 띄어쓰기도 중요한 문법 중 하나이다.
연산자가 어디에 있는지도 중요하지만, 연산자 앞과 뒤 중 어디에 공백이 있는지도 중요한 부분이다.

예를 들어 A != BA! = B 는 전혀 다른 의미이다. 또 A > B? A : B 는 잘못된 사용이며, 물음표를 B에서 띄어야 한다.

5.1 연산자의 종류

연산자는 종류가 굉장히 많다. 주요한 연산자를 하나씩 보자.

5.1.1 할당 연산자

값을 할당할 때 사용하는 연산자.

import Foundation

let a = 24
let b: Int?

b = a

print(b!)
// 24

5.1.2 산술 연산자

산술 연산자는 대체로 수학에서 쓰이는 연산자와 같은 역할을 수행한다.

import Foundation

let a = 24
let b = 30

print(a+b) // 54
print(a-b) // -6
print(a*b) // 720
print(a/b) // 0
print(a%b) // 24

🎨 스위프트의 나머지 연산과 나누기 연산자
스위프트에서는 부동소수점 타입의 나머지 연산까지 지원한다. 기존의 프로그래밍 언어에서는 나머지 연산자가 정수 타입만 지원하는 경우가 많았는데, 스위프트에서는 부동 소수점 타입도 나머지 연산을 할 수 있다.
let number: Double = 5.0
var result: Double = number.truncatingRemainder(dividingBy: 1.5) // 0.5
result = 12.truncatingRemainder(dividingBy: 2.5) // 2.0

나누기 연산은 기존의 프로그래밍 언어처럼 나머지나 소수점을 제외한 정수만을 결과값으로 반환한다.
var reuslt: Int = 5/3 // 1
result = 10 / 3 // 3

또한 스위프트는 데이터 타입에 굉장히 엄격 하므로 서로 다른 자료형끼리의 연산을 엄격히 제한한다. 서로 다른 자료형끼리의 연산을 실행하려면 값을 해당 타입으로 변환 후 연산해야 한다. 심지어 같은 정수 타입인 Int 타입과 UInt 타입끼리의 연산도 엄격히 제한된다.

truncatingRemainder(dividingBy:)

5.1.3 비교 연산자

두 값을 비교할 때 사용한다.

참조 비교 연산자

스위프트의 유일한 참조(Reference) 타입인 클래스의 인스턴스에서만 참조 비교 연산자를 사용할 수 있다.
스위프트의 기본 데이터 타입은 모두 구조체로 구현되어 있기 때문에
값 타입**이다. 그렇기 때문에, 값의 비교 연산에는 == 를 사용하고 클래스의 인스턴스인 경우에만 === 를 사용한다.

let valueA: Int = 3 
let valueB: Int = 5 
let valueC: Int = 5

let isSameValueAB: Bool = valueA == valueB // false
let isSameValueBC: Bool = valueB == valueC // true

let referenceA: SomeClass = SomeClass()
let referenceB: SomeClass = SomeClass()
let referenceC: SomeClass = referenceA

let isSameReferenceAB: Bool = referenceA === referenceB // false
let isSameReferenceAC: Bool = referenceA === referenceC // true`

5.1.4 삼항 조건 연산자

피연산자가 세 개인 삼항 조건 연산자이다.

import Foundation

var valueA: Int = 3
var valueB: Int = 5
var biggerValue: Int = valueA > valueB ? valueA : valueB // 5

valueA = 0
valueB = -3
biggerValue = valueA > valueB ? valueA : valueB // 0

var stringA: String = ""
var stringB: String = "String"
var resultValue: Double = stringA.isEmpty ? 1.0 : 0.0 // 1.0
resultValue = stringB.isEmpty ? 1.0 : 0.0 // 0.0

5.1.5 범위 연산자

값(수) 의 범위를 나타내고자 할 때 사용한다.

import Foundation

var A: Int = 3
var B: Int = 5

// MARK: - 폐쇄 범위 연산자
for i in A...B {
    print(i, terminator: " ") // 3,4,5
}

// MARK: - 반폐쇄 범위 연산자
for i in A..<B {
    print(i, terminator: " ") // 3,4
}

// MARK: - 단방형 범위 연산자
for i in A...100 {
    print(i, terminator: " ") // 3,4,5,6...
}

for i in 0...A {
    print(i, terminator: " ") // 0,1,2,3
}

for i in 0..<A {
    print(i, terminator: " ") // 0,1,2
}

5.1.6 부울 연산자

Bool 값의 논리 연산을 할 때 사용한다.

import Foundation

var A: Bool = false
var B: Bool = true

print(!B)     // false
print(A && B) // false
print(A || B) // true

5.1.7 비트 연산자

값의 비트 논리 연산을 위한 연산자이다.

import Foundation

var A: Int = 10
var B: Int = 20

var shiftA: Int = 1

print(~A)          // -11
print(A & B)       // 0
print(A | B)       // 30
print(A ^ B)       // 30
print(A >> shiftA) // 5
print(A << shiftA) // 20

TIP. 비트 시프트(이동)

시프트 연산자는 지정하는 수만큼 피연산자를 왼쪽(<<) 또는 오른쪽(>>) 으로 이동한다.
예를 들어 정수 4(0100)를 왼쪽으로 1 시프트 연산할 때 4 << 1로 표현해줄 수 있다.
왼쪽으로 비트를 이동했기 때문에 결과는 8(1000)이 된다.
반대로 오른쪽 1 시프트 연산을 하면 4 >> 1 로 표현할 수 있고, 결과는 2(0010)이 된다.

5.1.8 복합 할당 연산자

할당 연산자와 다른 연산자가 하는 일을 한 번에 할 수 있도록 연산자를 결합할 수 있다.
이를 복합 할당(Compound Assignment) 연산자라고 한다.

import Foundation

var A: Int = 10
var B: Int = 20

var shiftA: Int = 1

A += B
print(A) // 30

A -= B
print(A) // 10

A *= B
print(A) // 200

A /= B
print(A) // 10

A %= B
print(A) // 10

A <<= shiftA
print(A) // 20

A >>= shiftA
print(A) // 10

A &= B
print(A) // 0

A |= B
print(A) // 20

A ^= B
print(A) // 0

5.1.9 오퍼플로우 연산자

기존 프로그래밍 언어에서는 오퍼플로우(또는 언더플로우) 가능성이 있는 연산에 대해서는 따로 오버플로우에 대한 추가 알고리즘 및 로직 등을 설계하는 것이 일반적이었다. 스위프트는 기본 연산자를 통해 오버플로우에 대비할 수 있도록 준비해두었다. 오퍼플로우 연산자를 사용하면 오버플로우를 자동으로 처리한다.

예를 들어 UInt8 타입은 8비트 정수 타입으로 부호가 없는 양의 정수만을 표현하기 때문에 0 아래로 내려가는 계산을하면 런타임 오류가 발생한다. 그렇지만 오버플로우 빼기 연산을 하면 오류 없이 오버플로우 처리를 해준다.

그렇지만 오버플로우에 대한 이해 없이 사용한다면 엉뚱한 값을 구할 수도 있다.
따라서 오버플로우를 이해하고 사용해야 한다.

import Foundation

var unsignedInteger: UInt8 = 0
let errorUnderflowResult: UInt8 = unsignedInteger - 1 // 런타임 에러
let underflowedValue: UInt8 = unsignedInteger &- 1    // 255

unsignedInteger = UInt8.max                           // 255
let errorOverflowResult: UInt8 = unsignedInteger + 1  // 런타임 에러
let overflowedValue: UInt8 = unsignedInteger &+ 1     // 0

5.1.10 기타 연산자

import Foundation

let a: Int?
let b = 22

let optionalA: String? = "Hello" // 옵셔널 연산자
a = nil

print(a ?? b) // 22
print(-b) // -22
print(optionalA!) // Hello

정리

오늘은 연산자 챕터의 1번째 부분을 공부하였다.

  • 연산자의 종류는 연산되는 값과 연산자의 위치에 따라 구분된다.
  • 연산되는 값으로 단항, 이항, 삼항 등으로 구분한다.
  • 위치에 따라 전위, 중위, 후위 등으로 구분한다.
  • 연산자를 사용하면 굳이 코드로 구현하지 않아도 비교 또는 연산이 가능하다.
profile
iOS 정복중인 Tabber 입니다.

0개의 댓글