[스위프트 프로그래밍-5장] 연산자

sanghee·2021년 9월 26일
0
post-thumbnail

이 글은 스위프트 프로그래밍(3판, 야곰 지음)을 읽고 간단하게 정리한 글입니다. 책에 친절한 설명과 관련 예제 코드들이 있으므로 직접 사서 읽기를 추천합니다.

5.0 소개

스위프트의 연산자는 특정한 문자로 표현한 함수이다.

연산자의 연산 되는 값의 수에 따라 단항, 이항, 삼항 등으로 구분한다.

  • 단항 연산자: !A
  • 이항 연산자: A + B
  • 삼항 연산자: A ? B : C

연산자의 위치에 따라 전위, 중위, 후위 등으로 구분한다.

  • 전위 연산자: !A
  • 중위 연산자: A + B
  • 후위 연산자: A!

*띄어쓰기 또한 중요한 문법이다. 아래의 두 코드는 서로 다른 의미를 가진다.

A != B // A와 B는 같지 않다
A! = B // A의 강제 언래핑 값과 B는 같다

5.1 연산자의 종류

5.1.1 할당 연산자

값을 할당한다.

A = B // A에 B를 할당한다

5.1.2 산술 연산자

수학에서 쓰이는 연산을 수행한다.

A + B // A와 B를 더한다
A - B // A에서 B를 뺀다
A * B // A와 B를 곱한다
A / B // A에서 B를 나눈 값을 반환한다
A % B // A에서 B를 나눈 나머지를 반환한다

부동소수점 타입의 나머지 연산도 가능하다.

let double: Double = 5.0
let doubleResult: Double = double.truncatingRemainder(dividingBy: 1.5) // 0.5

let int: Int = 5
let intResult: Int = 5 / 2 // 2

5.1.3 비교 연산자

두 값을 비교하여 불리언 값을 반환한다.

A == B // A와 B가 같다
A != B // A와 B가 같지 않다

A >= B // A가 B보다 크거나 같다
A <= B // A가 B보다 작거나 같다
A > B // A가 B보다 크다
A < B // A가 B보다 작다

A === B // A와 B가 참조타입일 때 같은 인스턴스를 가리킨다
A !== B // A와 B가 참조타입일 때 다른 인스턴스를 가리킨다
A ~= B // A와 B의 패턴이 매치되는지 확인한다

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

let valueA: Int = 3
let valueB: Int = 4

let isSameValue = valueA == valueB // false
class SomeClass {
    ...
}

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

let isSameReferenceAB = referenceA === referenceB // false
let isSameReferenceAC = referenceA === referenceC // true

5.1.4 삼항 조건 연산자

피연산자가 세 개이다.

A == B ? 1 : 0 // A와 B가 같으면 1을, 아니면 0을 반환한다

5.1.5 범위 연산자

값의 범위를 나타낸다. 차례대로 폐쇄, 반폐쇄, 단반향 범위 연산자이다.

A...B // A 이상 B 이하의 범위이다
A..<B // A 이상 B 미만의 범위이다
A... // A 이상의 범위이다
...A // A 이하의 범위이다
..<A // A 미만의 범위이다

5.1.6 부울 연산자

불리언 값의 논리 연산을 한다.

!A // A 불리언을 반전한다
A && B // A와 B를 만족한다
A || B // A 또는 B를 만족한다

5.1.7 비트 연산자

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

~A // A의 비트를 반전한다
A & B // A와 B의 비트 AND 논리 연산을 실행한다
A | B // A와 B의 비트 OR 논리 연산을 실행한다
A ^ B // A와 B의 비트 XOR 논리 연산을 실행한다
A >> B // A의 비트를 왼쪽으로 B만큼 이동한다
A << B // A의 비트를 오른쪽으로 B만큼 이동한다

5.1.8 복합 할당 연산자

할당 연산자와 또 다른 연산자를 결합한다.

A += B // A와 B의 합을 A에 할당한다
A -= B // A와 B의 차를 A에 할당한다
A *= B // A와 B의 곱을 A에 할당한다
A /= B // A와 B의 나눈 값을 A에 할당한다
A %= B // A와 B의 나눈 나머지를 A에 할당한다

A <<= N // A의 비트를 왼쪽으로 N만큼 이동한 값을 A에 할당한다
A >>= N // A의 비트를 오른쪽으로 N만큼 이동한 값을 A에 할당한다
A &= B // A와 B의 비트 AND 연산 결과를 A에 할당한다
A != B // A와 B의 비트 OR 연산 결과를 A에 할당한다
A ^= B // A와 B의 비트 XOR 연산 결과를 A에 할당한다

5.1.9 오버플로 연산자

오버플로우를 처리한다. UInt8타입은 8비트 정수 타입으로 양의 정수만을 표현하기에 0 아래로 내려가는 계산을 하면 런타임 에러가 발생한다. 그렇기에 오버플로 연산자를 이용해 처리를 한다.

let uInt: UInt8 = 0
let errorResult: UInt8 = uInt - 1 // 런타임 에러 발생
let underflowedResult: UInt8 = uInt &- 1 // 255

5.1.10 기타 연산자

A ?? B // A가 nil이 아니면 A를, nil라면 B를 반환한다
-A // A의 부호를 변경한다
O! // 옵셔널 개체인 O를 강제 언래핑한다
V? // 옵셔널 값인 V를 안전하게 추출한다. 또는 V의 데이터 타입이 옵셔널임을 표현한다.

optionalInt값은 Int?타입이다.

let valueInt: Int = optionalInt ?? 0

5.2 연산자 우선순위와 결합방향

스위프트에서는 연산자 우선순위와 결합방향이 지정되어 있다. 우선순위가 높은 연산자가 먼저 실행되며 같은 우선순위라면 어느 방향부터 그룹지을 것인지 나타낸다. 연산자의 우선순위는 절대치가 아닌 상대적인 수치이다.

1 + 2 + 3 == ((1 + 2) + 3)
1 + 2 * 3 == (1 + (2 * 3))

5.3 사용자 정의 연산자

원하는 연산자를 정의할 수 있다. 아스키문자, /, =, -, +, !, *, %, <, >, &, |, ^, ?, ~를 사용한다. 마침표(.)를 사용자 정의 연산자로 사용할 때에는 맨 처음의 문자가 마침표여야 한다.

  • prefix: 상위 연산자
  • postfix: 후위 연산자
  • infix: 중위 연산자
  • operator: 연산자
  • associativity: 연산자 결합방향
  • precedence: 우선순위

5.3.1 전위 연산자 정의와 구현

Int 타입의 제곱과 세제곱을 구하는 후위 연산자를 만들어본다.

prefix operator **
prefix operator ***

prefix func ** (value: Int) -> Int {
    return value * value
}

prefix func *** (value: Int) -> Int {
    return value * value * value
}

let sqrtFive: Int = **5 // 25
let cubicFive: Int = ***5 // 125

스위프트 표준 라이브러리에 존재하는 연산자에 기능을 추가하고 싶다면 따로 연산자를 정의하지 않고 함수만 중복 정의한다.

prefix func ! (value: String) -> Bool {
    return value.isEmpty
}

let string: String = ""
let isEmptyString: Bool = !string // true

앞서 만든 사용자 전위 연산자를 다른 데이터 타입에서도 동작하도록 중복 정의할 수도 있다.

prefix operator **

prefix func ** (value: String) -> String {
    return value + value
}

let sqrtString: String = **"hello" // hellohello

5.3.2 후위 연산자 정의와 구현

하나의 피연산자에 전위 연산과 후위 연산이 있다면 후위 연산을 먼저 실행한다.

prefix operator **
postfix operator **

prefix func ** (value: Int) -> Int {
    return value * value
}

postfix func ++ (value: Int) -> Int {
    return value + value
}

let result: Int = **5++ // ((5 + 5) * (5 + 5)) // 100

5.3.3 중위 연산자 정의와 구현

위의 두 연산자와는 다르게 중위 연산자는 우선순위 그룹을 명시할 수 있다.

  • higherThan: 더 낮은 우선순위 그룹 이름을 명시한다.
  • lowerThan: 더 높은 우선순위 그룹 이름을 명시한다.
  • associativity: 결합방향(none, left, right)을 명시한다.
  • assignment: 옵셔널 체이닝과 관련됨. 할당방향(true, false)을 명시한다.
infix operator **

func ** (lhs: String, rhs: String) -> Bool {
    return lhs.contains(rhs)
}

let isContains: Bool = "abc"**"a" // true

*lhs와 rhs: lhs(Left Hand Side)와 rhs(Right Hand Side)이다. 연산자 함수를 만드는 경우 함수의 매개벼수를 적어줄 때 변수명으로 많이 사용한다.

다른 코드들처럼 전역함수로 구현했지만 타입 내부에 구현하는 것이 읽거나 이해하기에 더욱 쉽다.

class Car {
    var model: String
    var year: Int
    
    init(_ model: String, _ year: Int) {
        self.model = model
        self.year = year
    }
}

func == (lhs: Car, rhs: Car) -> Bool {
    return lhs.model == rhs.model
}

let carA = Car("A", 2010)
let carB = Car("A", 2011)
carA == carB /// true
struct Phone {
    var company: String?
    var model: String?
}

func == (lhs: Phone, rhs: Phone) -> Bool {
    return lhs.model == rhs.model
}

let phoneA = Phone(company: "Apple", model: "A")
let phoneB = Phone(company: "Apple", model: "B")
phoneA == phoneB // false

타입 내부에 타입 메서드로 변경한다. Car 클래스의 인스턴스끼리 연산을 하거나 Phone 구조체끼리 연산을 하는 == 함수를 타입 내부에 넣었다.

class Car {
    var model: String
    var year: Int
    
    init(_ model: String, _ year: Int) {
        self.model = model
        self.year = year
    }
    
    static func == (lhs: Car, rhs: Car) -> Bool {
        return lhs.model == rhs.model
    }
}

let carA = Car("A", 2010)
let carB = Car("A", 2011)
carA == carB /// true
struct Phone {
    var company: String?
    var model: String?
    
    static func == (lhs: Phone, rhs: Phone) -> Bool {
        return lhs.model == rhs.model
    }
}

let phoneA = Phone(company: "Apple", model: "A")
let phoneB = Phone(company: "Apple", model: "B")
phoneA == phoneB // false
profile
👩‍💻

0개의 댓글