Apple 제공 Swift 프로그래밍 가이드(4.2)의 Basic Operators 부분을 공부하며 정리한 글입니다. 개인적인 생각, 이해를 돕기 위한 예제도 조금 들어가있습니다.
들어가며
Swift 는 표준 C 연산자를 대부분 지원합니다.
Swift 의 연산자에는 코딩 오류를 미리 잡아주기 위한 몇 가지 특별한 점들이 있습니다. 다음은 그 예시 몇 가지입니다:
할당 연산자( = )는 어떤 값도 return 하지 않는다. ( if 조건문 안에서 = 대신 == 를 사용하지 않도록 )
산술 연산자( +, -, *, /, % 등 )는 오버플로우를 미리 감지하여 그런 연산을 허용하지 않는다. ( 연산결과가 저장될 변수에 오버플로우 값이 할당되지 않도록 )
또한 Swift 에는 범위 연산자(range operator)인 a..<b 와 a...b가 새롭게 추가되었습니다. 값의 범위를 표현하기 위한 shortcut으로 활용할 수 있습니다. Range를 지정할 때 유용합니다.
이 챕터에서는 일반적인 연산자만 다룹니다. 고급 연산자, 자신만의 커스텀 연산자를 정의하는 법, 커스텀 타입을 위해 일반 연산자를 적절히 구현하는 법 등은 Advanced Operators 에서 다루고 있습니다.
Teminology
Swift의 연산자에는 unary(단항), binary(이항), ternary(삼항) 세 종류가 있습니다.
Unary operator: single target (ex: -a). Unary prefix operator 는 타겟의 바로 앞에 붙습니다(ex: !b). Unary postfix operator 는 타겟의 바로 뒤에 붙습니다(ex: c!).
Binary operator: two targets (ex: 2 + 3). 타겟 두 개의 가운데에 있기 때문에 infix 라고 부릅니다.
Ternary operator: three targets. 스위프트에서의 삼항 연산자는 삼항 조건 연산자 (a ? b : c) 가 유일합니다.
Operator(연산자)가 영향을 끼치는 값들을 operand(피연산자)라고 부릅니다. 1 + 2 라는 식에서 + 기호는 binary operator 이고 1 과 2 는 두 개의 operand 입니다.
Assignment Operator
Assignment operator (a = b) : b의 값으로 a의 값을 초기화 또는 업데이트.
1 2 3 4 5 6 7 8 9 10 | 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 } | cs |
C, Objective-C 와 다르게 Swift 의 = 연산자는 어떤 값도 리턴하지 않습니다. 그래서 Swift 에서는 == 연산자(equal to operator)를 사용해야 할 조건문에 실수로 = 연산자를 사용하는 것을 방지할 수 있습니다.
Arithmetic Operators
산술연산자의 종류는 다음과 같으며 모든 Number 타입에 사용할 수 있습니다:
Addition (+)
Subtraction (-)
Multiplication (*)
Division (/)
1 2 3 4 | 1 + 2 // equals 3 5 - 3 // equals 2 2 * 3 // equals 6 10.0 / 2.5 // equals 4.0 | cs |
스위프트의 산술 연산자는 연산결과가 오버플로우 되는 것을 허용하지 않습니다. 그래서 필히 주의해야 할 경우가 생깁니다. 컴파일 시점에서는 알 수 없지만 런타임 때 오버플로우값이 연산결과로 할당되는 경우입니다. 컴파일 시점에서 알 수 있는 오버플로우 할당은 미리 컴파일러가 체크하여 오류로 잡아줍니다. 그러나 런타임 때만 알 수 있는 오버플로우 할당의 경우에는, 아예 어플리케이션이 죽어버리고 맙니다.
오버플로우 값 저장이 필요한 경우가 있을 수도 있는데, 이럴때는 오버플로우 연산자 (ex: a &+ b)를 사용하면 됩니다. Overflow Operators 에서 다루고 있습니다.
// 덧셈 연산자로 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) | cs |
Remainder Operator
Swift에서의 a % b 는 a = (b x 배수) + 나머지 에서의 나머지입니다. 즉 a % b 의 결과는 a 를 b 로 최대한 채운 후의 나머지입니다. 따라서 b 가 음수일 경우에도 b 의 부호는 무시됩니다. (9 % 4) 와 (9 % -4) 의 결과가 동일하다는 것입니다. 왜 이렇게 되는지 살펴봅시다.
1 2 3 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 | cs |
Unary Minus Operator & Unary Plus Operator
Unary Minus Operator (-) 와 Unary Plus Operator (+) 는 숫자 값의 부호에 관여합니다.
1 2 3 4 5 | let three = 3 let minusThree = -three // minusThree = -(3) = -3 let plusThree = -minusThree // plusThree = -(-3) = 3 let minusSix = -6 let alsoMinusSix = +minusSix // alsoMinusSix = +(-6) = -6 | cs |
위 예제에서도 드러나듯이, 사실 Unary Plus Operator(+)는 부호를 바꾸지 못하므로 사용하나 안하나 차이가 없습니다. 그러나 코드 상에서 Unary Minus Operator(-)를 사용할 때 함께 사용하면서 코드 상의 대칭을 맞출 수 있습니다.
Compound Assignment Operators
Assignment operator(=) 와 다른 연산자를 합친 compound assignment operator 를 알아봅시다. 하나의 예로 addition assignment operator (+=) 가 있습니다.
1 2 3 | var a = 1 a += 2 // a is now equal to 3 | cs |
a += 2 는 a = a + 2 와 같습니다. + 와 += 의 수행 시간은 동일합니다.
<Note>
Compound assignment operator 역시 아무 값을 반환하지 않습니다. let b = a += 2 와 같은 코드는 compile error 를 유발합니다.
Swift 스탠다드 라이브러리에서 제공하는 연산자들에 대한 정보를 더 얻고 싶다면 Operator Declarations 를 참조하세요.
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)
<Note>
Swift 는 또한 identity operators (A === B 와 A !== B) 를 제공합니다. 이 연산자는 A와 B의 object reference(객체 참조)가 동일한 object instance(객체 인스턴스)를 가리키고 있는지 검사할 때 사용합니다. 자세한 것은 Classes and Structures 를 참고하세요.
자세한 설명은 생략하고 예제로 알아보겠습니다. 단, 튜플 부분은 문서 외의 추가 설명도 넣었습니다.
1 == 1 // true because 1 is equal to 1 2 != 1 // true because 2 is not equal to 1 2 > 1 // true because 2 is greater than 1 1 < 2 // true because 1 is less than 2 1 >= 1 // true because 1 is greater than or equal to 1 2 <= 1 // false because 2 is not less than or equal to 1 let name = "world" if name == "world" { print("hello, world") } else { print("I'm sorry \(name), but I don't recognize you") } // Prints "hello, world", because name is indeed equal to "world". // 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 // 추가설명 ("blue", false) < ("purple", true) // Bool 타입으로는 크고 작음을 판단할 수 없다. compile error ("blue", false) == ("purple", true) // Bool 타입으로 같음을 판단할 수는 있다. return false | cs |
Ternary Conditional Operator
Ternary Conditional Operator (삼항 조건 연산자)는 question ? answer1 : answer2 의 폼을 가지며, 세 부분으로 구성되는 특별한 연산자입니다.
if question { answer1 } else { answer2 } // 위 코드를 단축하여 표현한 것이 question ? answer1 : answer2 | cs |
삼항 조건 연산자의 장점은 코드를 간결하게 보이게 하고, 임시변수를 줄일 수 있다는 것입니다. 하지만 남용하거나 복합 구문 안에 삽입하는 것은 가독성을 떨어트리니 주의해야 합니다. 개인적으로는 한 줄 안에 끝날 때만, 그리고 단독으로 쓰일 때만(컨텍스트가 거기서 끝날 때만?) 사용하는 편입니다.
let contentHeight = 40 let hasHeader = true let rowHeight: Int if hasHeader { rowHeight = contentHeight + 50 } else { rowHeight = contentHeight + 20 } // 위 코드를 아래와 같이 단축할 수 있습니다 let contentHeight = 40 let hasHeader = true let rowHeight = contentHeight + (hasHeader ? 50 : 20) // rowHeight is equal to 90 | cs |
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? // defaults to nil var colorNameToUse = userDefinedColorName ?? defaultColorName print(colorNameToUse) // "red" userDefinedColorName = "green" colorNameToUse = userDefinedColorName ?? defaultColorName print(colorNameToUse) //"green" | cs |
<Note>
삼항 연산자에는 short-circuit evaluation 이 적용됩니다. 즉, a ?? b 에서 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)
One-Sided Ranges([a...], [..<b] 등) : 한 쪽 방향으로 갈 수 있는 만큼 가게 됨. (ex: [..<2] 는 0,1)
범위 연산자는 for 문에서 유용하게 쓰일 수 있을 것 같은데, 첫 인덱스가 1인지 0인지에 따라 골라쓰면 될 것 같습니다. 예를들어 1부터 N까지 숫자를 출력하는 for문에서는 a...b를 쓰는 것이 유용하고, 배열을 도는 for문에서는 인덱스가 0부터 count-1 까지니까 a..<b 를 쓰는 것이 유용합니다. Swift 4 에서 추가된 One-Sided Range Operator 도 경우에 따라 유용하게 사용할 수 있을 것 같네요(total count 를 미리 계산해서 범위지정 하지 않아도 되는..등등? 개발하면서 테스트해보겠습니다!).
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 번 돌게된다 for name in names[2...] { print(name) } // Brian // Jack for name in names[...2] { print(name) } // Anna // Alex // Brian for name in names[..<2] { print(name) } // Anna // Alex // One-sided ranges can be used in other contexts, not just in subscripts. let range = ...5 range.contains(7) // false range.contains(4) // true range.contains(-1) // true | cs |
Logical Operators
다음 세 가지의 논리 연산자를 통해 Boolean logic 값인 true 와 false 를 다룰 수 있습니다:
Logical NOT (!a)
Logical AND (a && b)
Logical OR (a || b)
자세한 설명은 생략합니다. 참고로 Swift 에서 && 와 || 는 left-associative 입니다(여러 개가 한번에 결합되어 있을 때 왼쪽부터 차례로 연산된다는 의미입니다). 따라서 순서에 상관없이 특정 연산이 먼저 이루어져야 할 때는 괄호를 사용해야 합니다. 의도를 명확히 드러내기 위해 괄호를 쓰는 것도 좋습니다.
let allowedEntry = false if !allowedEntry { print("ACCESS DENIED") } // Prints "ACCESS DENIED" let enteredDoorCode = true let passedRetinaScan = false if enteredDoorCode && passedRetinaScan { print("Welcome!") } else { print("ACCESS DENIED") } // Prints "ACCESS DENIED" let hasDoorKey = false let knowsOverridePassword = true if hasDoorKey || knowsOverridePassword { print("Welcome!") } else { print("ACCESS DENIED") } // Prints "Welcome!" if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword { print("Welcome!") } else { print("ACCESS DENIED") } // Prints "Welcome!" if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword { print("Welcome!") } else { print("ACCESS DENIED") } // Prints "Welcome!" | cs |
'Swift 공식 가이드 > Swift 4' 카테고리의 다른 글
Collection Types (0) | 2018.09.05 |
---|---|
Strings and Characters (0) | 2018.06.20 |
The basics (0) | 2018.03.02 |
A Swift Tour (0) | 2017.10.17 |
Version Compatibility (0) | 2017.10.04 |