본문 바로가기

학습 노트/Swift (2021)

020 ~ 031. Operator (연산자) + 실습

기본

Syntax

A + B

A와 B는 Operend (피연산자)
+는 Operend (연산자)

구분

+A A + B A + B : C
단항연산자 이항연산자 삼항연산자
조건연산자
//단항연산자는 공백 없이 붙여 쓴다.
//⭕️
+A

//❌
+ A


//이항연산자는 공백을 사용해 띄어 쓴다.
//⭕️
A + B

//❌
A+B


//연산자 형식에 따른 명칭
//prefix Operator
+A

//postfix Operator
A+

//infix Operator
A + B

기본적으로 곱과 나눗셈을 덧셈과 뺄셈보다 우선 수행한다.

 

Arithmetic Operator (산술 연산자)

Addiction (덧셈)

Syntax

A + B

Subtraction (뺄셈)

Syntax

A - B

Multiple (곱셈)

Syntax

A * B

Division (나눗셈)

Syntax

A / B

Reminder, Modulo (나머지)

Syntax

A % B

floating point 간의 연산은 지원하지 않는다.
이 경우 truncatingReminder를 사용한다.

Syntax

A.truncatingReminder(dividingBy: B)

 

Overflow Operator (오버플로우)

Swift에선 Overflow를 기본적으로 허용하지 않는다.
따라서 결과를 예측할 수 없는 경우 무리하지 말고 큰 자료형을 사용하자.

단, 의도적으로 사용하고 싶은 경우 Overflow 연산자를 사용해야 한다.

Overflow Addiction Operator (오버플로우 덧셈)

Syntax

A &+ B
let a: Int8 = Int8.max
let b: Int8 = a &+ 1
print(b)

b의 값은 -128이다.

Overflow Subtraction Operator (오버플로우 뺄셈)

Syntax

A &- B
let a: Int8 = Int8.min
let b: Int8 = a &- 1
print(b)

 

b의 값은 127이다.

Overflow Multiplication Operator (오버플로우 곱셈)

Syntax

A &* B
let a: Int8 = Int8.max
let b: Int8 = a &* 2
print(b)

 

b의 값은 -2이다.

왜?

Int8의 저장 범위는 '-128 ~ 127' 이다.
따라서 Int8.max의 값은 '127'이고, 2진수로 변경하면 다음과 같다.

  SB 64 32 16 8 4 2 0
a 0 1 1 1 1 1 1 1

'a &* 2'의 연산은 다음과 같이 진행된다.

  SB 64 32 16 8 4 2 0
a   0 1 1 1 1 1 1
a   0 1 1 1 1 1 1
b 0 1 1 1 1 1 1 0

b는 SignBit가 1이므로 음수이고, 나머지 Bit를 2의 보수화 하면 2이다.
따라서 'a &* 2'의 결과는 '-2' 이다.

 

Comparison Operator (비교 연산자)

Equal to Operator

Syntax

A == B

Not Equal to Operator

Syntax

A != B

Grater than Operator

Syntax

A > B

Greater than or Equal Operator

Syntax

A >= B

Less than Operator

Syntax

A < B

Less than or Equal Operator

Syntax

A <= B

 

Logical Operator (논리 연산자)

Logical NOT Operator

Syntax

!A
입력 출력
true false
false true

Logical AND Operator

Syntax

A && B
입력A 입력B 출력
true ture true
true false flase
false true false
false false false

Logical OR Operator

Syntax

A || B

입력A 입력B 출력
true ture true
true false true
false true true
false false false

Ternary Conditional Operator (조건 연산자)

Syntax

Condition ? expr1 : expr2
Condition1 ? expr1 : (Condition2 ? expr2 : expr3)

조건 연산자의 경우 if나 switch로 대체가 가능하다.
2번과 같이 조건이 많아지는 경우 코드의 가독성이 나빠지기 때문에 이를 활용한다.

//if로 구현하기
if Condition1 {
    expr1
} else {
    if Condition2 {
        expr2
    } else {
    expr3
    }
}

//switch로 구현하기
switch {
    case Condition1:
        expr1
    case Condition2:
        expr2
    default:
        expr3
}

 

조금 길어 보여도 명확하다.

Short Circuit Evaluation

Swift는 논리식을 평가할 때 필요한 최소한의 연산만 수행한다.

Logical And Operator

입력A 입력B 출력
true ture true
true false flase
false true false
false false false

And 연산은 좌변이 false이면 항상 false이다.

Logical Or Operator

입력A 입력B 출력
true ture true
true false true
false true true
false false false

Or 연산은 좌변이 true인 경우 항상 true이다.

이렇게 좌변 만으로 결과가 도출 되는 경우 우변은 수행하지 않고 논리식을 평가한다.
이 연산 속도 면에서 상당한 이득이지만 다음과 같은 경우 문제가 되기도 한다.

var a = 1
var b = 1

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

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

if inputA && inputB {
    
}

print(a)
print(b)

 

결과

2
1

이 경우 좌변인 inputA의 결과가 flase이기 때문에 inputB는 실행되지 않는다.
따라서 결과는 a=2, b=1 이다
만약 'a+b'의 연산을 추가적으로 수행하고자 한다면 3의 결과가 나오므로 일반적이지 않다.

var a = 1
var b = 1

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

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

let resultA = updatedLeft()
let resultB = updatedRight()

if resultA && resultB {
    
}

print(a)
print(b)

 

결과

2
2

이렇게 inputA와 inputB를 먼저 실행하고 논리식을 진행하면
결과는 a=2, b=2가 되고, 비로소 'a+b'를 추가적으로 수행했을 때 4라는 일반적인 결과가 된다.

이렇게 코드를 실행했을 때 값이나 상태가 변하는 것을 'Side effect'라고 한다.
직역하면 '부작용'의 의미이지만 프로그래밍에선 마냥 부정적인 의미는 아니다.

논리식을 다룰 땐 이 'Side effect'를 염두해 두고 논리식 내에서의 side effect를 최소화 하는 것이 좋다.

 

Bitwise Operator

Bitwise Not Operator

Syntax

~A
A 0 0 1 0 0 0 1 1
  1 1 0 1 1 1 0 0

0과 1을 바꾼다

Bitwise And Operator

Syntax

A & B
A 0 0 1 0 0 0 1 1
B 0 0 0 1 1 0 1 0
  0 0 0 0 0 0 1 0

모두 1이면 1이다.

Bitwise Or Operator

Syntax

A | B
A 0 0 1 0 0 0 1 1
B 0 0 0 1 1 0 1 0
  0 0 1 1 1 0 1 1

둘 중 하나라도 1이면 1이다.

Bitwise XOr Operator

Syntax

A ^ B
A 0 0 1 0 0 0 1 1
B 0 0 0 1 1 0 1 0
  0 0 1 1 1 0 0 1

둘 중 하나만 1이면 1이다.

Bitwise Left Shift Operator

Syntax

A << n

한 비트 옮길 수록 2배씩 커진다.
곱 연산보다 연산속도가 빠르지만 차이가 미비하다.

Logical Shift

A   0 0 1 0 0 0 1 1  
  0 0 1 0 0 0 1 1 0  

왼쪽으로 한 비트씩 옮긴다.

Bitwise Right Shift Operator

Syntax

A >> n

한 비트 옮길 때 마다 1/2이 된다.

LogicalShift

A   0 0 1 0 0 0 1 1  
    0 0 0 1 0 0 0 1 1

오른쪽으로 한 비트씩 옮긴다.

Arithmetic Shift

A   1 0 1 0 0 0 1 1  
    1 1 0 1 0 0 0 1 1

단, SignBit를 가졌을 경우 0이 아닌 원래의 SignBit를 채운다.

 

Assigment Operator

Syntax

A = B

할당 연산자는 값을 반환하지 않는다.

 

Compound Assigment Operator

Addiction Assigment Operator

Syntax

A += B
A = A + B

Subtraction Assigment Operator

Syntax

A -= B
A = A - B

Multiplication Assigment Operator

Syntax

A *= B
A = A * B

Division Assigment Operator

Syntax

A /= B
A = A / B

Modulo Assigment Operator

Syntax

A %= B
A =A % B

Bitwise AND Assigment Operator

Syntax

A &= B
A = A & B

Bitwise OR Assignment Operator

Syntax

A |= B
A = A | B

Bitwise XOR Assignment Operator

Syntax

A ^= B
A = A ^ B

Bitwise Left Shift Assginment Operator

Syntax

A <<= B
A = A << B

Bitwise Right Shift Assginment Operator

Syntax

A >>= B
A = A >> B

 

Range Operator

Closed Range Operator

Syntax

A ... B
A...
...A
let list = [1, 2, 3, 4, 5]

//[3, 4]
list[2 ... 3]

//[3, 4, 5]
list[2...]

//1, 2, 3, 4]
list[...3]

index로 접근하기 때문에 시작이 1이 아닌 0임을 유의하자.

Half-open Range Operator

Syntax

A ..< B
..<A
let list = [1, 2, 3, 4, 5]

//[1, 2]
list[..<2]

//[1, 2, 3]
list[0 ..< 3 ]

 

Operator methods

연산자를 새로 생성하는 게 아닌 기존 연산자의 확장이다.

Syntax

static func operator(parameters) -> ReturnType {
    statemnets
}

Operator methods는 연산자가 가지고 있는 결합규칙과 우선순위를 바꾸지 않는다.
가능한한 원래의 기능과 동일하거나 유사한 형태로 구현해야 한다.
그러지 않을 경우 코드의 가독성이 떨어지고 논리적 오류가 발생할 가능성이 커진다.

//Double 두 개 짜리 구조체 생성
struct Point {
    var x = 0.0
    var y = 0.0
}

let p1 = Point(x: 10, y: 100)
let p2 = Point(x: 20, y: 50)

//p1 == p2 -> error

//구조체를 비교하는 방법 선언
extension Point: Equatable {
    static func ==(lhs: Point, rhs: Point) -> Bool {
        return (lhs.x == rhs.x) && (lhs.y == rhs.y)
    }
}

 

구조체에서 생성해도 되지만, 보통은 Extension으로 구현한다.
다만 위의 Operator method는 다음과 같이 단축 되기도 한다.

extension Point: Equatable {
    //static func ==(lhs: Point, rhs: Point) -> Bool {
    //    return (lhs.x == rhs.x) && (lhs.y == rhs.y)
    }
}

 

구조체의 자료형이 동일하고, 해당 자료형이 동일한 Equatable protocol을 사용하는 경우 컴파일러가 자동으로 구현하기도 한다.

//구조체 부호 변경 구현하기
extension Point {
    static prefix func -(pt: Point) -> Point { //단항연산자의 경우 연산자의 위치를 선언해 줘야 한다.
        return Point(x: -pt.x, y: -pt.y)
    }
}

//구조체 증가 연산자 구현
extension Point {
    static postfix func ++(pt: inout Point) -> Point{ //단항연산자의 경우 연산자의 위치를 선언해 줘야 한다.
        let ret = pt
        pt.x += 1
        pt.y += 1
        return ret
    }
}

 

'++'의 구현의 경우 값을 변경해야 하기 때문에 inout prameter를 사용해야 한다.

 

Custom Operator

첫번째로 조합해야 하는 기호

/, =, -, +, !, *, %, <, >. &, |, ^, ? ~
단독사용 불가능한 기호

( ), { }, [ ], ".", ",", :, ;, =, @, #, &, ->, ₩, ?, !

연산자 선언하기 (반드시 global scope에서 선언해야 한다.)

Syntax

prefix operator ▢
postfix operator ▢
infix operator ▢
//prefix
▢A

//postfix
A▢

//infix
A ▢ B

 

연산자는 가능한 간단한 형태로 선언할 것.
기족 연산자와 모호함이 없도록 선언할 것.

사용 예시

prefix operator +++
    
extension Int {
    static prefix func +++(num: inout Int) {
        num += 100
    }
}
    
var a = 1
+++a
결과

101

Precedence Groups (우선순위 그룹)

Syntax

infixs operator operator: PrecenenceGroup
precedencegroup Name {
    higherThan: lowerGroupName
    lowerThan: HigherGroupName
    associativity: left, right, none
}

우선순위는 ':'뒤에 기입하는 것에 대한 조건임을 기억해야 한다.

Swift에서 지원하는 기본 연산자들의 우선순위 그룹은 다음과 같다.

Operators Precedence Groups
+, - AddictionPrecednece
*, / MultiplicationPrecedence
<, <=, >, >=, ==, != ComparisonPrecedence
=, +=, -=, *=, /= AssignmentPrecedence
precedencegroup Myprecedence {
    higherThan: AdditionPrecedence
}
    
infix operator *+*: Myprecedence
    
extension Int {
    static func *+* (left: Int, right: Int) -> Int {
    return (left * right) + (left * right)
    }
}
    
1 *+* 2 + 3
결과

7

우선순위 그룹의 method는 최소 하나는 작성해야 한다.

 

배운 것을 바탕으로 '+='를 구현해 보자

//Double 두 개 짜리 구조체 생성
struct Point {
    var x = 0.0
    var y = 0.0
}

//'+=' 구현
extension Point {
    static func +=(lhs: inout Point, rhs: inout Point) ->Point { //값을 변경하여 다시 전달해야 하기 때문에 inout parameter 사용
        lhs.x += rhs.x
        lhs.y += rhs.y
        return Point(x: lhs.x, y: rhs.y)
    }
}

var p6 = Point(x: 10, y: 20)
var p7 = Point(x: 1, y: 1)

p6 += p7

p6.x
p6.y

p7.x
p7.y
결과

11
21

1
1

Log

2021.08.04.
블로그 이전으로 인한 글 옮김 및 수정