티스토리 뷰
지금부터 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 에서 사용되는 주요 연산자 및 커스텀 연산자 추가 방법까지 간략하게 알아보았습니다.
'swift > 문법' 카테고리의 다른 글
05. Swift 문법 - Loop Statements (0) | 2020.10.11 |
---|---|
04. Swift 문법 - Conditional Statements (0) | 2020.10.11 |
02. Swift 문법 - Literals, Data Types (0) | 2020.10.09 |
01. Swift 문법 - Variables and Constants, Scope (0) | 2020.10.08 |
00. Swift 문법 - Token, Expressions, Statements 등 기초 지식 (0) | 2020.10.08 |
- Total
- Today
- Yesterday
- Baekjoon
- Combination
- CodeDeploy
- 조합
- ECR
- ionic
- 소수
- array
- string
- 프로그래머스
- 순열
- AWS
- 수학
- programmers
- CodeCommit
- java
- BFS
- map
- cloudfront
- Algorithm
- sort
- SWIFT
- search
- spring
- 에라토스테네스의 체
- EC2
- Dynamic Programming
- CodePipeline
- permutation
- DFS
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |