Apple 제공 Swift 프로그래밍 가이드(3.0.1)의 Basic Operators 부분을 공부하며 정리한 글입니다. 개인적인 생각도 조금 들어가있습니다.
들어가며
스위프트는 표준 C 연산자를 대부분 지원한다.
스위프트의 연산자에는 코딩 오류를 미리 잡아주기 위한 몇 가지 특별한 점들이 있다. 다음은 그 예시 몇 가지이다.
할당 연산자(=)는 어떤 값도 return 하지 않는다. ( if 조건문 안에서 = 대신 == 를 사용하는 것을 방지하기 위해)
산술 연산자(+, -, *, /, % 등)는 오버플로우를 미리 감지하여 그런 연산을 허용하지 않는다. (연산결과가 저장될 변수에 오버플로우된 값이 할당됨을 방지하기 위해)
또한 스위프트에는 범위 연산자 a..<b 와 a...b가 새롭게 추가되었다. 값의 범위를 표현하기 위한 shortcut으로 활용할 수 있다. Range를 지정할 때 편할 것 같다.
Teminology
Swift의 연산자는 unary(단항), binary(이항), ternary(삼항) 세 종류가 있다.
Unary operator : single target (ex: -a). 타겟의 바로 앞에 붙는 prefix (ex: !b) 와 타겟의 바로 뒤에 붙는 postfix (ex: c!) 가 있다.
Binary operator : two targets (ex: 2 + 3). 타겟 두개의 가운데에 있는 infix.
Ternary operator : three targets. 스위프트에서의 삼항 연산자는 삼항 조건 연산자 (a ? b : c) 가 유일하다.
Assignment Operator
a = b : b의 value로 a의 value를 초기화 또는 업데이트
let b = 10 // initialize b
var a = 5 // initialize a
a = b // update a
let (x, y) = (1, 2) // x = 1, y =2
// 주의 : 할당 연산자는 return value가 없다. 다음과 같은 구문은 compile error
if x = y {
// not valid
}
예전에 Objective-C로 작성된 코드 중에서, if 문의 조건문 안에서 if (변수 = [변수에 들어갈 값을 계산하는 메서드]) 같은 코드를 보았던 적이 있는데, 이제 이런 식의 코딩은 스위프트에서 할 수 없다.
Arithmetic Operators
산술연산자의 종류는 다음과 같으며 모든 Number 타입에 사용할 수 있다.
Addition (+)
Subtraction (-)
Multiplication (*)
Division (/)
스위프트의 산술 연산자는 연산결과가 오버플로우 값이 되는 것을 허용하지 않는다. 그래서 필히 주의해야 할 경우가 생긴다. 컴파일 시점에서는 알 수 없지만 런타임 때 오버플로우값이 연산결과로 할당되는 경우이다. 컴파일 시점에서 알 수 있는 오버플로우 할당은 미리 컴파일러가 체크하여 오류로 잡아준다. 그러나 런타임 때만 알 수 있는 오버플로우 할당의 경우에는, 아예 어플리케이션이 죽어버리고 만다.
오버플로우 값 저장이 필요한 경우가 있을 수도 있다. 이럴때는 오버플로우 연산자 (ex: a &+ b)를 사용하면 된다. (나중에 다룹니다)
// 덧셈 연산자로 String 을 더할 수 있다.
"hello, " + "world" // equals "hello, world”
// 주의 : Character+Character, String+Character 조합은 불가능
let a:Character = "a"
let b:Character = "b"
let ab = a + b // complie error (Character+Character)
let ab2 = "a" + b // compile error (String+Character)
let ab3 = "a" + "b" // ok. (String+String)
let c = "c" // String Type 으로 판단
let ac = "a" + c // ok. (String+String)
Remainder Operator
Swift에서의 a % b 는 a = (b x 배수) + 나머지 에서의 나머지이다. 즉 a % b 의 결과는 a를 b로 최대한 채운 후의 나머지이다. 따라서 b가 음수일 경우에도 b의 부호는 무시된다. (9 % 4)와 (9 % -4)의 결과가 동일하다는 것이다. 왜 이렇게 되는지 살펴보자.
9 % 4 // 9 = (4 x 2) + 1. 따라서 1
9 % -4 // 9 = (-4 x -2) + 1. 따라서 1
-9 % 4 // -9 = (4 x -2) + -1. 따라서 -1
-9 % -4 // -9 = (-4 x 2) + -1. 따라서 -1
Unary Minus Operator & Unary Plus Operator
단항 마이너스 연산자(-) 와 단항 플러스 연산자(+) 는 숫자 값의 부호에 관여한다.
let three = 3
let minusThree = -three // minusThree = -(3) = -3
let plusThree = -minusThree // plusThree = -(-3) = 3
let minusSix = -6
let alsoMinusSix = +minusSix // alsoMinusSix = +(-6) = -6
사실 단항 플러스 연산자(+)는 어떤 경우에도 부호를 바꾸지 못하므로 사용할 필요가 없다. 그러나 코드 상에서 단항 마이너스 연산자(-)를 사용할 때 함께 사용하면서 코드 상의 대칭을 맞출 수는 있다.
Compound Assignment Operators
할당 연산자(=) 앞에 다른 연산자를 붙여서 값 할당 + 추가 연산을 할 수 있다.
예를들어 a += 2 는 a = a + 2와 같다.
이 경우에도 연산자에서 값을 반환하지 않는다. 따라서 let b = a += 2 와 같은 코드는 compile error.
Comparison Operators
스위프트는 C의 모든 표준 연산자를 지원한다.
Equal to (a == b)
Not equal to (a != b)
Greater than (a > b)
Less than (a < b)
Greater than or equal to (a >= b)
Less than or equal to (a <= b)
또한 identity operators (=== 와 !==) 를 제공한다. 이 연산자는 두 개의 Object reference(객체 참조)가 동일한 Object instance(객체 인스턴스)를 가리키고 있는지 검사할 때 사용한다. (나중에 다룹니다.)
// tuple 의 comparison
// (1) 같은 index에 같은 타입이 있어야 비교가 가능 (또한 비교가 가능한 타입이어야 한다)
// (2) 비교하는 순서는 left to right, 한번에 한 인덱스만, 두 개의 값이 같을 경우 다음 인덱스를 비교
(1, "zebra") < (2, "apple") // 1 < 2 를 해보고 곧바로 return true
(3, "apple") < (3, "bird") // 첫 번째 element 값이 같았으므로 "apple" < "bird" 를 해보고 return true
(4, "dog") == (4, "dog") // 모든 element가 같은 것을 확인한 뒤 return true
// 추가설명
(true, 1) < (true, 1) // Bool 타입으로는 크고 작음을 판단할 수 없다. compile error
(true, 1) == (true, 1) // Bool 타입으로 같음을 판단할 수는 있다. return true
Ternary Conditional Operator
삼항 조건 연산자는 스위프트에서 사용되는 유일한 삼항 연산자이다.
if question {
answer1
}
else {
answer2
}
위 코드를 question ? answer1 : answer2 한 줄로 단축할 수 있다.
삼항 조건 연산자의 장점은 코드를 간결하게 보이게 하고, 임시변수를 줄일 수 있다는 것. 하지만 남용하거나 복합 구문 안에 삽입하는 것은 가독성을 떨어트리니 주의해야 한다. 개인적으로는 한 줄 안에 끝날 때만, 그리고 단독으로 쓰일 때만 쓰면 좋을 것 같은데 코딩을 하다보면 마음처럼 잘 되지 않기도 한다...
Nil Coalescing Operator
a ?? b
optional 변수 a에 만약 값이 들어있다면 그 값을 사용하고, a가 nil이라면 b를 사용. 풀어쓰면 a != nil ? a! : b 이다. 따라서 다음 두 개가 반드시 전제된다.
a는 optional 타입
b의 타입은 a에 저장될 수 있는 타입
이 연산자를 사용하면 optional 변수를 사용할 때 nil 체크를 하고, 언랩핑하고, nil일때의 default 값을 반환하는 등의 과정을 코드 상에서 짧고 간결하게 나타낼 수 있다. 가독성 상승. optional 변수를 사용하면서 많이 쓰게 될 것 같은 느낌이 든다.
let defaultColorName = "red"
var userDefinedColorName: String?
var colorNameToUse = userDefinedColorName ?? defaultColorName
print(colorNameToUse) // "red"
userDefinedColorName = "green"
colorNameToUse = userDefinedColorName ?? defaultColorName
print(colorNameToUse) //"green"
<note> short-circuit evaluation : a가 nil이 아니라면 b는 계산되지 않는다. 예를 들어 a ?? (b + c) 에서 a에 이미 값이 있다면 (b + c) 연산은 하지도 않는다는 것.
Range Operators
Swift에는 값의 범위를 표현하는 범위 연산자가 두 종류 있다.
Closed Range Operator(a...b) : a부터 시작해서 b까지. (ex: 1...3 은 1,2,3)
Half-Open Range Operator(a..<b) : a이상 b 미만. (ex: 1..<3 은 1,2)
범위 연산자는 for 문에서 유용하게 쓰일 것이다. 첫 인덱스가 1인지 0인지에 따라 골라쓰면 될 것 같다. 예를들어 1부터 N까지 숫자를 출력하는 for문에서는 a...b를 쓰는 것이 유용하고, 배열을 도는 for문에서는 인덱스가 0부터 count-1까지니까 a..<b 를 쓰는 것이 유용하다.
for index in 1...5 {
print("\(index) times 5 is \(index * 5)")
}
// 1 times 5 is 5
// 2 times 5 is 10
// 3 times 5 is 15
// 4 times 5 is 20
// 5 times 5 is 25
// a...b 는 b-a+1 번 돌게된다
let names = ["Anna", "Alex", "Brian", "Jack"]
let count = names.count
for i in 0..<count { // 0..<4
print("Person \(i + 1) is called \(names[i])")
}
// Person 1 is called Anna
// Person 2 is called Alex
// Person 3 is called Brian
// Person 4 is called Jack
// a..<b 는 b-a 번 돌게된다
Logical Operators
다음 세 가지의 논리 연산자를 통해 Boolean 값을 다룰 수 있다. 자세한 설명은 생략한다.
Logical NOT (!a)
Logical AND (a && b)
Logical OR (a || b)
<note> 스위프트에서 && 와 || 는 left-associative 이다. 여러 개가 한번에 결합되어 있을 때 왼쪽부터 차례로 연산된다. 따라서 순서에 상관없이 특정 연산이 먼저 이루어져야 할 때는 괄호를 사용해야 한다. 또한 좀 더 의도를 명확히 드러내기 위해 괄호를 쓰는 것도 좋다.
'Swift 공식 가이드 > Swift 3' 카테고리의 다른 글
Control Flow (0) | 2017.02.15 |
---|---|
Collection Types - Dictionary (0) | 2017.02.11 |
Collection Types - Set (0) | 2017.02.11 |
Collection Types - Array (0) | 2017.02.11 |
Strings and Characters (2) | 2017.02.05 |