연산자는 값을 확인을하거나, 변경을하거나, 결합하는데 사용하는 특별한 심볼이나 구이다. 예를 들어 +
연산자는 두개의 숫자를 더한다. let i = i + 2
그리고 논리 연산자 AND(&&)
는 두 불린 값을 결합한다. enteredDoorCode && passedRetinaScan
스위프트는 이미 알려진 C언어의 연산자를 지원하고, 일반적인 코딩에러를 제거하기 위해 여러 기능을 개선한다. 할당 연산자인 =
는 값을 리턴하지 않으므로, 동등 연산자 ==
의 의도로 사용한 경우 잘못 사용되는 것을 방지한다. 산술 연산자 +, -, *, /, % 등
은 값의 오버플로우를 허가하지 않고, 허용된 값의 범위안에 예기치 않은 결과 값을 피해준다.
스위프트는 또한 C언어에는 없는 범위 연산자를 제공한다 a..<b, a...b
이번 챕터에서는 스위프트의 일반 연산자들에 대해 설명한다.
연산자는 단항, 이항 또는 삼항이다.
-a, !b, c! 등
단항 앞에 붙을수도 있고 뒤에 붙을수도 있다. 2 + 3 등
두 항목사이에 연산자를 넣는다.a ? b : c
(삼항 조건 연산자)연산자에 의해 영향을 받는 값들을 피연산자(operands
)이다. 1 + 2
에서 1과 2는 피연산자이고, + 는 연산자이다.
a = b
로 사용한다. a값을 b로 초기화하거나 업데이트 한다.
let b = 10
let a = 5
a = b
// a is now eqaul to 10
let (x, y) = (1, 2)
// x is equal to 1, and y is equal to 2
// 튜플의 대입 연산자 사용 예
// C와 Objective-C와는 다르게 스스로 값을 리턴하지 않으므로 아래 예는 유효 x
if x = y {
// This isn`t valid, because x = y doesn`t return a value.
}
이 특징은 동등 연산자로 사용하려고 하는 경우에 실수로 할당 연산자를 사용한 경우를 예방해준다.
기본적인 연산자들이 존재한다. + - * /
C와 Objective-C와 다르게 스위프트의 산술 연산자는 오버플로우를 기본적으로 허용하지 않는다. +
연산자는 문자열에서도 사용이 가능하다.
"hello, " + "world" // equals "hello, world"
나머지 연산자를 제공한다 %
로 사용함
NOTE
나머지 연산자(%)는 다른 언어에서 모듈러 연산자(modulo operator)로 알려져 있으나, 이는 엄격히 말해서 음수에 대한 스위프트의 동작은 모듈러 연산이 아닌 나머지 라는 것을 의미한다.9 % 4 // equals 1
a = (b * some multiplier) + remainder
-9 % 4 // eqauls -1
-9 = (4 * -2) + -1
b의 음수형태는 무시되므로, `a % b 와 a % -b` 는 언제나 같다.
### Unary Minus Operator (단항 빼기 연산자)
피연산자가 음수라면 양수로, 양수라면 음수로 변환함
```swift
let three = 3
let minusThree = -three // minusThree equals -3
let plusThree = -minusThree // plusThree equals 3, or "minus minus three"
단항 더하기 연산자는 아무런 값을 반환하지 않지만, 코드에서 음수에 단항 빼기 연산자를 사용할 때 양수에 대칭을 위해 사용할 수 있다.
C와 같이 스위프트는 복합 대입 연산자를 제공한다 (=
와 같이 사용함)
var a = 1
a += 2
// a is now eqaul to 3
// a +=2는 a = a + 2 라는 의미와 같다.
a == b
a != b
a > b
a < b
a >= b
a <= b
NOTE
스위프트는 또한 두개의 객체 참조가 동일한 객체 인스턴스를 참조하는지에 대한 여부를 테스트하는 ID 연산자를 제공한다===, !==
// 사용 예
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".
// 튜플에서의 비교
(1, "zebra") < (2, "apple") // true because 1 is less than 2; "zebra" and "apple" aren't compared
(3, "apple") < (3, "bird") // true because 3 is equal to 3, and "apple" is less than "bird"
(4, "dog") == (4, "dog") // true because 4 is equal to 4, and "dog" is equal to "dog"
위의 코드와 같이 같은 타입과 같은 수의 튜플 끼리는 비교가 가능하다. 왼쪽 요소부터 비교하여서 결과를 정함. 첫번째 예시에서 1은 2보다 작으므로 뒤에 요소는 비교하지 않고 결과를 반환한다.
튜플은 해당 튜플의 각 값에 연산자를 적용할 수 있을 때만 비교가 가능하다. 밑의 코드와 같이 Bool
끼리는 <
연산자는 사용이 불가하므로 에러가 발생한다.
("blue", -1) < ("purple", 1) // OK, evaluates to true
("blue", false) < ("purple", true) // Error because < can't compare Boolean values
NOTE
스위프트의 표준 라이브러리에는 튜플의 7개 미만의 요소만 비교하는 연산자가 제공되어 있다. 7개 이상의 요소를 비교하려면 스스로 비교 연산자를 구현해야만 한다
삼항 조건 연산자는 question ? answer1 : answer2
로 사용하는 특별한 연산자이다. 질문에 참/거짓에 따라 두 식중 하나를 평가하는 축약형이다. 만약 질문이 참이라면 answer1
을 아니라면, answer2
를 반환한다.
// 이것의 축약형과 같다
if question {
answer1
} else {
answer2
}
let contentHeight = 40
let hasHeader = true
let rowHeight = contentHeight + (hasHeader ? 50 : 20)
// rowHeight is equal to 90
삼항 조건 연산자는 두개의 표현식을 고려할 때 유용한 축약식이다. 하지만 주의해서 사용해야 한다. 이 간결함을 남용한다면 코드를 읽는게 어려워 질 수 있다. 삼항 조건 연산자의 여러 인스턴스를 하나의 복합문으로 결합하는 것을 피해라.
nil 결합 연산자 a ?? b
는 옵셔널 a
가 값을 포함하면 언래핑하고, a
가 nil
이면 기본값 b
를 반환한다. a
는 이 표현식에서 항상 옵셔널 타입이다. 표현식 b
의 타입은 a
와 같아야만 한다.
a != nil ? a! : b
// nil결합 연산자는 위를 짧게 표현이 가능하다.
NOTE
a
의 값이nil
이 아니라면b
의 값은 평가되지 않는다. 이를 단락평가라고한다(short-circuit evaluation)
let defaultColorName = "red"
var userDefinedColorName: String? // defaults to nil
var colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName가 nil이기 때문에 colorNameToUse의 초기값은 red로 할당된다.
userDefinedColorName = "green"
colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName isn't nil, so colorNameToUse is set to "green"
스위프트는 값을 짧게 표현하기 위해 범위 연산자를 포함한다.
a...b
와 같이 사용한다. a
는 반드시 b
보다 작아야 한다. (a와 b를 포함한다)
// for-in 반복문에서 유용하게 사용이 된다
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
와 같이 사용한다. a
는 반드시 b
보다 작아야한다. (b 포함 x, a와 b가 같다면 빈 범위가 나온다)
// 제로베이스 리스트(0부터 시작하는)인 배열에서 유용하게 사용된다.
let names = ["Anna", "Alex", "Brian", "Jack"]
let count = names.count
for i in 0..<count {
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, ..<a, a..., ...
으로 사용된다. ...a
는 처음부터 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
// 마지막 값을 입력해서 범위에 어떤 값이 포함되는지 확인이 가능하다
let range = ...5
range.contains(7) // false
range.contains(4) // true
range.contains(-1) // true
논리연산자는 C언어에 논리 연산자를 기초로 하고 있다.
!a
a && b
a || b
true
를 false
로 false
를 true
로 만들어 주는 역할을 한다
let allowedEntry = false
if !allowedEntry {
print("ACCESS DENIED")
}
// Prints "ACCESS DENIED"
위 예와 같이 불린 상수나 변수이름을 신중하게 선택하면 코드의 가독성에 도움을 준다
두 값이 전부 true
인 경우에 true
를 반환한다. 처음 값이 false
라면 두번째 값은 평가 하지않고 넘어간다 (단락평가)
let enteredDoorCode = true
let passedRetinaScan = false
if enteredDoorCode && passedRetinaScan {
print("Welcome!")
} else {
print("ACCESS DENIED")
}
// Prints "ACCESS DENIED"
하나라도 true
이면 true
를 반환 한다. 처음 값이 true
인 경우에도 마찬가지로 두번째 값은 평가하지 않는다.
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!"
위의 예는 다음과 같다 "enteredDoorCode
와 passedRetinaScan
를 비교하고 true
라면 true
를 반환한다. false
라면 hasDoorKey
가 true
일시에 true
로 반환 false
라면 knowsOverridePassword
가 true
일시 true
반환 아니라면 false
를 반환한다."
NOTE
스위프트의 여러 논리 연산자가 있는 복합 표현식에는 맨 왼쪽부터 평가를 시작한다 (왼쪽 우선결합)
괄호를 명시하는것은 엄격하게는 필요하지 않은 경우에 복잡한 표현식을 읽기 쉽게 만드려는 의도에서 유용하다.
// 위의 예에서 괄호를 명시하여 읽기 쉽게 하였다
if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword {
print("Welcome!")
} else {
print("ACCESS DENIED")
}
// Prints "Welcome!"
결과 값은 변하지 않았지만, 전체 의도가 좀 더 읽는이들에게 명확해졌다. 가독성은 항상 간결함보다 우선해야 한다 의도를 명확하게 하려는 경우에 괄호를 사용해라 !
대체로 아는것들이 많은 챕터였다. 처음 본 부분은 nil-결합 연산자나 범위연산자 정도였기 때문에 편하게 읽은 챕터이다.