티스토리 뷰

swift/문법

03. Swift 문법 - Operators

DevBee 2020. 10. 10. 22:25

지금부터 Swift 의 Operators (연산자)에 대해 알아보겠습니다.

 

1. Operator Basic

연산자는 연산에 사용하는 문자를 의미하며 영어로는 Operator 라고 합니다. 간단하게 보면 1 + 2 에서 + 가 연산자고 1과 2는 피연산자 (Operand)라고 합니다.

 

연산자의 종류는 다음과 같습니다.

- Unary Operator: 단항 연산자, 피연산자가 하나 => 예) +a
- Binary Operator: 이항 연산자, 피연산자가 2개 => 예) a + b
- Ternary Operator: 삼항 연산자(조건 연산자), 피연산자 3개 => 예) a ? b : c

 

사용 시 표기 방법이 있는데 단항 연산자로 사용하는 경우 연산자와 피연산자 사이에 공백이 없어야 됩니다.

+a
+ a // 에러

 

이항 연산자로 사용할 때는 공백이 있어도 되고 없어도 되지만 연산자 양쪽을 맞춰야 합니다.

a + b
a+b
a+ b // 에러
a +b // 에러

 

삼항 연산자도 공백을 추가하여 사용하는 것이 좋습니다.

a ? b : c

 

연산자를 위치에 따라 구분하면 다음과 같습니다.

- 단항 연산자의 경우

    - Prefix Operator: 전치 연산자, 피연산자 앞에 연산자 => +a
    - Postfix Operator: 후치 연산자, 피연산자 뒤에 연산자 => a+
- 이항 연산자의 경우
    - Infix Operator: 피연산자 사이에 연산자 => a + b

 

연산자에는 우선 순위 (Precedence) 가 존재합니다.

a + b * c   // * 연산자의 우선 순위가 높으므로 b * c 계산 후 그 결과값과 a 를 + 한다.
(a + b) * c // () 를 사용하면 우선 순위를 변경할 수 있다. a + b 를 먼저 계산

 

가장 안쪽 괄호부터 순서대로 계산 괄호가 중복되는 경우 가장 안쪽 괄호부터 순서대로 계산됩니다.

연산자에는 결합 규칙 (Associativity)도 있는데 다음과 같습니다.
- 왼쪽에서 오른쪽으로 계산 (Left Associative)
- 오른쪽에서 왼쪽으로 계산 (Right Associative)

3 - 2 - 1   // 결과: 0, 일반적으로는 왼쪽부터 오른쪽으로 계산을 합니다.
3 - (2 - 1) // 결과: 2, 오른쪽부터 계산하고 싶은 경우 ()를 사용합니다.

 

즉, 우선 순위와 결합 규칙은 정해진 규칙이 있지만 ()를 통해 직접 지정이 가능합니다.

연산을 할때는 식에 포함된 자료형 타입을 모두 맞춰야 연산이 가능합니다.
- Int + Int = Int
- Double + Double = Double

 

2. Arithmetic Operators

/*
 # Arithmetic Operators
 산술 연산
 */

let a = 12
let b = 34

// + 연산자
// Unary Plus Operator
// ex) +a
+a  // 결과: 12
+b  // 결과: 34

// Addition Operator
// ex) a + b
a + b  // 결과: 46

// - 연산자
// Unary Minus Operator
// ex) -a
// 부호를 바꾸는 역할을 함. 음수 -> 양수, 양수 -> 음수
-a  // 결과: -12
-b  // 결과: -34

// Subtraction Operator
// ex) a - b
a - b  // 결과: -22

// x 연산자
// Multiplication Operator
// ex) a * b
a * b  // 결과: 408

// / 연산자
// Division Operator
// ex) a / b
// a 에서 b 를 나눈 몫이 출력
a / b  // 결과: 0
b / a  // 결과: 2

let c = Double(a)
let d = Double(b)
c / d  // 결과: 0.3529411764705883
d / c  // 결과: 2.833333333333333

// % 연산자
// Remainder Operator (Modulo Operator)
// ex) a % b
// 나머지를 출력
// 실수의 경우 지원하지 않음.
a % b  // 결과: 12
b % a  // 결과: 10

// c % d
c.truncatingRemainder(dividingBy: d)  // 결과: 12

// Overflow
// 자료형에 저장할 수 있는 값의 범위를 벗어나는 문제를 말함.
// Swift 에서는 이를 허용하지 않음
// let num: Int8 = 9 * 9 * 9 - 에러
let num: Int = 9 * 9 * 9

// 컴파일 타임에 확인할 수 없는 에러도 있음.
// 가능하다면 큰 자료형을 사용하는 것이 좋음
// Overflow Operator
Int8.min  // 결과: -128
Int8.max  // 결과: 127

// Arithmetic operation '127 + 1' (on type 'Int8') results in an overflow
// let numOver: Int8 = Int8.max + 1 - 에러

// Overflow Addition Operator
// ex) a &+ b
let aa: Int8 = Int8.max  // 결과: 127
let bb: Int8 = aa &+ 1   // 결과: -128

// Overflow Subtraction Operator
// ex) a &- b
let cc: Int8 = Int8.min  // 결과: -128
let dd: Int8 = cc &- 1   // 결과: 127

// Overflow Multiplication Operator
// ex) a &* b
let e: Int8 = Int8.max &* 2  // 결과: -2

Overflow 연산자의 경우 메모리 최대 값을 초과하게 되면 메모리의 최소 값으로 돌아가고 메모리의 최소 값보다 작아지면 최대 값으로 이동한다는 특징이 있다는 것만 알아두고 넘어가겠습니다.

 

3. Comparison Operators

/*
 # Comparison Operators
 비교 연산자
 이항 연산자이며 결과값은 항상 Bool 타입
 */

let a = 12
let b = 34

// Equal to Operator
// ex) a == b
// 피연산자의 자료형을 같게 해야 비교 가능
a == b              // 결과: false
"Swift" == "swift"  // 결과: false (문자열의 대문자, 소문자를 구분하기 때문)

// Not Equal to Operator
// ex) a != b
a != b              // 결과: true

// Greater than Operator
// ex) a > b
a > b               // 결과: false
"swift" > "Swift"   // 결과: true, ASCII 문자로 비교

// Greater than or equal to Operator
// ex) a >= b
a >= b              // 결과: false

// Less than Operator
// ex) a < b
a < b               // 결과: true

// Less than or equal to Operator
// ex) a <= b
a <= b              // 결과: true

 

4. Logical Operators

/*
 Logical Operators
 논리 연산자
 참과 거짓을 구분하는 연산자로 모든 피연산자가 Bool 타입이고 결과도 Bool 타입
 */

// Logical NOT Operators
// ex) !a
// 참 -> 거짓, 거짓 -> 참
!true        // 결과: false

let a = 12
let b = 34

a < b        // 결과: true
!(a < b)     // 결과: false

// Logical AND Operators
// ex) a && b
// a, b 모두 true 인 경우에만 true 를 반환
a > 30 && b % 2 == 0  // 결과: false

true && true          // 결과: true
true && false         // 결과: false
false && true         // 결과: false
false && false        // 결과: false

// Logical OR Operators
// ex) a || b
// a, b 둘 중 하나만 true 라도 true 를 반환
a > 30 || b % 2 == 0  // 결과: true

true || true          // 결과: true
true || false         // 결과: true
false || true         // 결과: true
false || false        // 결과: false

 

5. Ternary Conditional Operator

/*
 Ternary Conditional Operator
 삼항 연산자
 */

// ex) condition ? expr1 : expr2
//     조건 ? 참인 경우 : 거짓인 경우

// 조건 1. condition 에 Bool 표현식이 와야 함
// 조건 2. 2번째 피연산자와 3번째 피연산자의 타입이 동일해야 함

let hour = 12

hour < 12 ? "am" : "pm"  // 결과: pm

// if 문으로 변경 가능
if hour < 12 {
    "am"
} else {
    "pm"
}

// 예제
// hour < 11 "Good Morning"
// hour < 17 "Good Afternoon"
// "Good Evening"
hour < 11 ? "Good Morning" : (hour < 17 ? "Good Afternoon" : "Good Evening")

 

6. Short-circuit Evaluation, Side Effect

/*
 # Short-circuit Evaluation
 단락 평가
 : 논리식에서 결과를 도출하는데 필요한 최소한의 코드를 실행하는 것을 의미
 */

// && 연산자의 경우 처음 피연산자가 false 이면 다음 피연산자 확인 없이 바로 false
// false &&

// || 연산자의 경우 처음 피연산자가 true 이면 다음 피연산자 확인 없이 바로 true
// true ||

var a = 1
var b = 1

func updateLeft() -> Bool {
    a += 1
    return true
}

func updateRight() -> Bool {
    b += 1
    return true
}

if updateLeft() || updateRight() {
    
}

a  // 결과: 2
b  // 결과: 1

// --------------------------------------------------------------- //

func updateLeft() -> Bool {
    a += 1
    return false
}

func updateRight() -> Bool {
    b += 1
    return true
}

if updateLeft() || updateRight() {
    
}

a  // 결과: 2
b  // 결과: 2

if updateLeft() && updateRight() {
    
}

a  // 결과: 3
b  // 결과: 2
// Side Effect
// 코드의 실행 결과로 인해 값 또는 상태가 바뀌는 것을 의미

a = 1
b = 1

// 논리식에 Side Effect 를 발생시킬 수 있는 코드가 포함되어 있으면
// 논리적이 오류가 발생할 가능성이 높기 때문에 주의!
let resultA = updateLeft()
let resultB = updateRight()

if resultA && resultB {
    
}

a  // 결과: 2
b  // 결과: 2

 

7. Bitwise Operators

/*
 Bitwise Operators
 비트 연산자
 - 연산 속도가 빠르고 짦은 코드로 복잡한 로직을 구현할 때도 사용됨.
 - 간단한 예로는 비트 플래그로 옵션을 지정할 때 주로 사용되고,
 - 하드웨어 드라이버를 만들거나, 임베디드 프로그램을 만들거나,
 - 그래픽 처리를 하거나, 암호화 코드를 구현할 때도 자주 사용됨.
 */

// Bitwise NOT Operator
// ex) ~a
// 0 -> 1, 1 -> 0

let a: UInt8 = 0b0000_0010     // 결과: 2
~a                             // 결과: 253
0b1111_1101                    // 결과: 253

// Bitwise AND Operator
// ex) a & b
// 모든 비트가 1인 경우에만 1이 됨

let b: UInt8 = 0b0010_0010     // 결과: 34
let c: UInt8 = 0b1100_1110     // 결과: 206
b & c                          // 결과: 2
0b0000_0010                    // 결과: 2

// Bitwise OR Operator
// ex) a | b
// 하나의 비트만 1이라도 1이 됨

b | c                          // 결과: 238
0b1110_1110                    // 결과: 238

// Bitwise XOR Operator (Exclusive OR)
// a ^ b
// 비트가 서로 다르면 1이 되고 같으면 0이 됨

b ^ c                          // 결과: 236
0b1110_1100                    // 결과: 236

// Logical Shift (부호를 신경쓰지 않는 경우)
// Bitwise Left Shift Operator
// a << n
// 메모리에 저장된 비트를 왼쪽으로 이동시키기
// 새로 추가되는 비트는 0임. 가장 왼쪽 비트는 이동하면서 사라짐.
// a * (n * 2) 와 같은 효과

let d: UInt8 = 0b0010_0010      // 결과: 34
d << 1                          // 결과: 68
0b0100_0100                     // 결과: 68

// Bitwise Right Shift Operator
// a >> n
// 메모리에 저장된 비트를 오른쪽으로 이동시키기
// 새로 추가되는 비트는 0임. 가장 오른쪽 비트는 이동하면서 사라짐.
// a / (n * 2) 와 같은 효과

d >> 1                          // 결과: 17
0b0001_0001                     // 결과: 17

// Arithmetic Shift (최상위 비트를 부호로 써야 하는 경우)
// a << n 의 경우는 Logical Shift 와 동일
// a >> n 의 경우 기존에 있던 Sign Bit가 젤 왼쪽에 채워짐

 

8. Assignment Operators

/*
 # Assignment Operator
 할당 연산자
 값을 저장하는 역할
 결과를 리턴하지 않음
 */

// a = b
let a = 12
var b = 34
b = a

// lvalue: 메모리 공간을 나타내는 표현식
// rvalue: 저장할 값을 나타내는 표현식
// lvalue 는 rvalue 로도 사용 가능함. ex) b = a(o)
// 반대는 안됨.                     ex) 12 = a (x)

// Compound Assignment Operators
// 복합할당 연산자

// Addition Assignment Operator
// ex) a += b (a = a + b)

var c = 1
var d = 2
c += d

// Subtraction Assignment Operator
// ex) a -= b (a = a - b)
c -= d

// Multiplication Assignment Operator
// ex) a *= b (a = a * b)
c *= d

// Division Assignment Operator
// ex) a /= b (a = a / b)
c /= d

// Modular Assignment Operator
// ex) a %= b (a = a % b)

// Bitwise AND Assignment Operator
// ex) a &= b (a = a & b)

// Bitwise OR Assignment Operator
// ex) a |= b (a = a | b)

// Bitwise XOR Assignment Operator
// ex) a ^= b (a = a ^ b)

// Bitwise Left Shift Assignment Operator
// ex) a <<= b (a = a << b)

// Bitwise Right Shift Assignment Operator
// ex) a >>= b (a = a >> b)

 

9. Range Operators

/*
 # Range Operators
 범위 연산자
 */

// Closed Range Operator
// ex) a ... b (b 가 범위에 포함됨)
//     a...
//     ...a

1 ... 10
// 10 ... 1 - 에러 발생 (오름차순 범위만 허용됨)
(1 ... 10).reversed() // 내림차순을 쓰려면 reversed() 함수 사용

12.34 ... 56.78 // 실수도 가능

var sum = 0
for num in 1 ... 10 {
    sum += num
}
sum  // 결과: 55

let list = ["A", "B", "C", "D", "E"]
list[2...] // 2번째 인덱스부터 끝까지, 결과: ["C", "D", "E"]
list[...2] // 0부터 2번째 인덱스까지, 결과: ["A", "B", "c"]
// 고정된 범위가 확실한 경우에만 upperBound 생략!

// Half-Open Range Operator
// ex) a ..< b (b 가 범위에 포함이 안됨)
//     ..<a

sum = 0
for num in 1 ..< 10 {
    sum += num
}
sum  // 결과: 45

list        // 결과: ["A", "B", "C", "D", "E"]
list[..<2]  // 결과: ["A", "B"]

let range = 0 ... 5
// 범위에 포함되는지 확인할 때 사용하는 함수
range.contains(7) // 결과: false
range.contains(1) // 결과: true

let range2 = ...5  // 단독으로 사용하면 lowerBound 는 무한대
range2.contains(-1)      // 결과: true
range2.contains(Int.min) // 결과: true

 

10. Operator Methods

/*
 # Operator Methods
 */

// static func operator(parameters) -> ReturnType {
//      statement
// }

// 연산자 부분에는 이미 있는 연산자가 와야함
// 연산자가 가지고 있는 우선순위와 결합 규칙을 바꾸지 않으며
// 원래 연산자의 기능과 동일한 기능을 수행하도록 만들어야 합니다.
// 원래 연산자의 parameters, ReturnValue 의 타입을 같게 해야 합니다.

"a" == "a"

struct Point {
    var x = 0.0
    var y = 0.0
}

extension Point: Equatable {
    // Complier 가 자동으로 추가
    // static func ==(lhs: Point, rhs: Point) -> Bool {
        // return (lhs.x == rhs.x) && (lhs.y == rhs.y)
    // }
}

let p1 = Point(x: 12, y: 34)
let p2 = Point(x: 67, y: 89)

p1 == p2  // 결과: false
p1 != p2  // 결과: true

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

let p3 = -p1
p3.x      // 결과: -12
p3.y      // 결과: -34

extension Point {
    static postfix func ++(pt: inout Point) -> Point {
        let ret = pt
        pt.x += 1
        pt.y += 1
        return ret
    }
}

var p4 = Point(x: 1.0, y: 2.0)
let p5 = p4++

p4.x       // 결과: 2
p4.y       // 결과: 3

p5.x       // 결과: 1
p5.y       // 결과: 2

 

11. Custom Operators

연산자를 직접 만들 수도 있는데 이를 만들 때는 다음과 같은 유의 사항을 확인해야 합니다.

 

1. 연산자를 가능한 단순한 형태로 선언해야 한다.
2. 기존에 있는 연산자와 함께 사용했을 때 모호함이 없도록 선언해야 한다.

 

기본적으로 새로운 연산자는 다음과 같은 형태로 선언합니다.

- prefix operator operator

- postfix operator operator

- infix operator operator

 

새로운 연산자를 만들 때 연산자 이름 규칙이 있는데 다음과 같습니다.

- Reserved Tokens 으로 아래와 같은 문자는 단독으로 사용이 불가합니다.

   (, ), {, }, [, ], ., ,, :, ;, =, @, #, &(prefix operator), ->, `, ?, !(postfix operator)

- First Character 로 처음에 올 수 있는 문자가 정해져 있습니다.

   /, =, -, +, !, *, %, <, >, &, |, ^, ?, ~

 

새로운 연산자는 다음과 같은 형태로 구현할 수 있습니다.

static prefix func operator(parameters) -> ReturnType {

    statements

}

 

static postfix func operator(parameters) -> ReturnType {

    statements

}

 

static func operator(parameters) -> ReturnType {

    statements

}

 

새로운 연산자를 추가할 때 연산자 우선 순위를 지정할 수 있는데 다음과 같은 형태를 가집니다.

먼저 기본 연산자 우선 순위 그룹을 살펴보면 다음과 같습니다.

연산자 Precedence Group
+ - AdditionPrecedence Group
* / MultiplicationPrecedence Group
< <= > >= == !=  ComparisionPresedence Group
= += -= *= /=  AssignmentPresedence Group

연산자 우선 순위는 다음과 같은 형태로 지정할 수 있습니다.

infix operator operator: PrecedenceGroup

 

precedencegroup Name {

    higherThan: LowerGroupName

    lowerThan: HigherGroupName

    associativity: associativity

}

 

실제 예제를 살펴보면 다음과 같습니다.

prefix operator +++

extension Int {
    static prefix func +++(num: inout Int) {
        num += 2
    }
}

var a = 1
+++a

precedencegroup MyPrecedence {
    higherThan: AdditionPrecedence
}

infix operator *+*: MyPrecedence

extension Int {
    static func *+*(left: Int, right: Int) -> Int {
        return (left * right) + (left * right)
    }
}

1 *+* 2 + 3

지금까지 Swift 에서 사용되는 주요 연산자 및 커스텀 연산자 추가 방법까지 간략하게 알아보았습니다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함