안녕하세요. 개발하는 정주입니다.
오늘은 기본 연산자(Basic Operators)를 정리하려고 합니다.
* 공식 문서 내용을 한 줄 한 줄 읽는 것에 의의를 두었습니다.
* 파파고의 힘을 빌려 번역했으며 잘못된 번역이 있다면 댓글로 알려주세요.
목차
새로 배운 점
- Swift가 C++로 이루어진 만큼 C와 연산자가 유사한 부분이 많다는 점
- 튜플의 연산
서론
연산자는 값을 할당, 연산, 결합하는 특수 기호입니다.
예를 들어, 덧셈 연산자(+)는 i = 1 + 2에서와 같이 두 숫자를 더하고
논리 곱 연산자(&&)는 entryDoorCode && passedRetinaScan과 같이 두 개의 Boolean 값을 결합합니다.
Swift는 기본적으로 C언어의 연산자를 지원하며 코딩 오류를 줄이기 위해 몇 가지 기능을 개선하였습니다.
할당 연산자(=)가 값을 반환하지 않으므로 연산자(==)의 등호를 사용할 때 값이 잘못 사용되는 것을 방지할 수 있고
산술 연산자(+, -, *, /, % 등)는 값을 오버플로우를 탐지하고 허용하지 않습니다.
Swift는 C에서 찾을 수 없는 a..<b와 a...b같은 범위 연산자를 제공하는데, 이는 값의 범위를 편하게 표기할 수 있습니다.
용어(Terminology)
연산자에는 단항, 이항, 삼항 연산자가 있습니다.
단항 연산자는 -a처럼 단일 대상을 가집니다.
단항 prefix 연산자는 대상 바로 앞에 나타나고(예: !b), 단항 postfix 연산자는 대상 바로 뒤에 나타납니다(예: c!).
이항 연산자는 2 + 3 같이 두 대상 사이에 위치하는 연산자입니다.
삼항 연산자는 세 가지 대상을 가진다. C와 마찬가지로 스위프트는 삼항 연산자(a ? b : c)만 존재합니다.
할당 연산자(Assignment Operator)
할당 연산자(a = b)는 a의 값을 b의 값으로 초기화하거나 변경합니다. 상수와 변수 모두 사용 가능합니다.
let b = 10
var a = 5
a = b
// a is now equal to 10
여러 값이 있는 튜플이 있으면 해당 요소를 한 번에 할당할 수 있습니다.
let (x, y) = (1, 2)
// x is equal to 1, and y is equal to 2
C 및 Objective-C의 할당 연산자와 달리 Swift의 할당 연산자 자체는 값을 반환하지 않습니다. 다음 문법은 유효하지 않습니다.
if x = y {
// This isn't valid, because x = y doesn't return a value.
}
이 기능은 실수로 등호 연산자(==) 대신 할당 연산자(=)를 쓰는 것을 방지합니다.
x = y가 유효하지 않은 경우 Swift는 코드에서 이러한 종류의 휴먼 오류를 방지하는 데 도움이 됩니다.
산술 연산자(Arithmetic Operators)
스위프트는 아래 4개의 표준 산술 연산자를 지원합니다.
1 + 2 // equals 3
5 - 3 // equals 2
2 * 3 // equals 6
10.0 / 2.5 // equals 4.0
C와 Objective-C의 산술 연산자와는 달리 스위프트 연산자는 기본적으로 값이 오버플로 되는 것을 허용하지 않습니다.
Swift의 오버플로 연산자(예: a &+ b)를 사용하여 오버플로 동작 값을 지정할 수 있습니다.
덧셈 연산자는 문자열 연결도 지원합니다.
"hello, " + "world" // equals "hello, world"
나머지 연산자(Remainder Operator)
나머지 연산자(a % b)는 a에 b의 배수가 얼마나 들어갈지를 계산하고 남은 값(나머지)을 반환합니다.
The remainder operator (%) is also known as a modulo operator in other languages.
However, its behavior in Swift for negative numbers means that,
strictly speaking, it’s a remainder rather than a modulo operation.
나머지 연산자는 다음과 같이 작동합니다.
9 % 4를 계산하려면 먼저 9에 적합할 4의 배수를 계산해야 합니다. 두 개의 4를 9 안에 적합시킬 수 있으며 나머지는 1입니다.
스위프트에서는 다음과 같이 씁니다.
9 % 4 // equals 1
-9 % 4 // equals -1
a % b에 대한 답을 결정하기 위해, % 연산자는 다음 방정식을 계산하고 나머지를 출력으로 반환합니다.
단항 음수 연산자(Unary Minus Operator)
숫자 값의 부호는 단항 음수 연산자 -를 사용하여 전환할 수 있습니다. 단항 음수 연산자(-)는 공백 없이 연산자 바로 앞에 붙습니다.
let three = 3
let minusThree = -three // minusThree equals -3
let plusThree = -minusThree // plusThree equals 3, or "minus minus three"
단항 양수 연산자(Unary Plus Operator)
단항 양수 연산자(+)는 값에 아무런 영향을 주지 않습니다.
let minusSix = -6
let alsoMinusSix = +minusSix // alsoMinusSix equals -6
단항 양수 연산자는 값에 아무 변화가 없지만 음수에 단항 음수 연산자를 사용할 때 양수에 대한 대칭을 제공하는 데 사용할 수 있습니다.
합성 할당 연산자(Compound Assignment Operators)
Swift는 C와 마찬가지로 할당(=)을 다른 작업과 결합하는 합성 할당 연산자를 제공합니다. 한 가지 예는 덧셈 할당 연산자(+=)입니다.
var a = 1
a += 2
// a is now equal to 3
"a += 2"라는 표현은 "a = a + 2"를 줄인 것입니다.
실제로 추가 및 할당은 두 태스크를 동시에 수행하는 하나의 연산자로 결합됩니다.
합성 할당 연산자가 값을 반환하지 않습니다. 예를 들어, "let b = a += 2"는 쓸 수 없습니다.
비교 연산자(Comparison Operators)
Swift는 다음과 같은 비교 연산자를 지원합니다.
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)
Swift는 또한 두 개의 객체 참조가 모두 동일한 인스턴스를 참조하는지 확인하는
두 개의 ID 연산자(=== 및 !==)를 제공합니다.
각 비교 연산자는 Bool 값을 반환합니다.
1 == 1 // true because 1 is equal to 1
2 != 1 // true because 2 isn't 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 isn't less than or equal to 1
비교 연산자는 if 문같은 조건문에 사용됩니다.
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보다 작기 때문에 (1, "zebra")는 (2, "apple") 보다 작은 것으로 간주됩니다.
비교가 튜플의 첫 번째 요소에 의해 결정되기 때문에 "zebra"가 "apple"보다 작지 않다는 것은 중요하지 않습니다.
튜플의 첫 번째 요소가 동일하면 두 번째 요소가 비교됩니다.
이게 두 번째 줄과 세 번째 줄에서 일어나는 일입니다.
각 튜플의 각 값에 연산자를 적용할 수 있는 경우에만 튜플을 지정된 연산자와 비교할 수 있습니다.
예를 들어, 아래 코드처럼 String과 Int 값은 모두 < 연산자로 비교할 수 있으므로
두 튜플의 유형(String, Int)을 비교할 수 있습니다.
반대로, 타입의 두 튜플(String, Bool)은 < 연산자를 Bool 값에 적용할 수 없기 때문에 < 연산자로 비교할 수 없습니다.
("blue", -1) < ("purple", 1) // OK, evaluates to true
("blue", false) < ("purple", true) // Error because < can't compare Boolean values
스위프트 표준 라이브러리는 7개 미만의 요소를 가진 튜플에 대한 튜플 비교 연산자를 포함하고 있습니다.
튜플을 7개 이상의 원소와 비교하려면 직접 비교 연산자를 구현해야 합니다.
삼항 연산자(Ternary Conditional Operator)
삼항 조건 연산자는 의 형태를 취하는 세 부분으로 된 특수 연산자입니다.
question ? answer1 : answer2
'question'이 참인지 거짓인지에 따라 두 가지 표현 중 하나를 반환하는 연산자입니다.
question이 참이면 answer1의 결과 값을 반환하고, 그렇지 않으면 answer2 결과 값을 반환합니다.
삼항 조건 연산자는 아래의 코드에 대한 축약형입니다.
if question {
answer1
} else {
answer2
}
다음은 테이블 행의 높이를 계산하는 예시입니다.
let contentHeight = 40
let hasHeader = true
let rowHeight = contentHeight + (hasHeader ? 50 : 20)
// rowHeight is equal to 90
위의 예는 아래 코드를 축약한 것입니다.
let contentHeight = 40
let hasHeader = true
let rowHeight: Int
if hasHeader {
rowHeight = contentHeight + 50
} else {
rowHeight = contentHeight + 20
}
// rowHeight is equal to 90
첫 번째 예제에서 삼항 조건 연산자를 사용하는 것은 rowHeight를 코드의 한 줄에서 올바른 값으로 설정할 수 있음을 의미하며, 이는 두 번째 예제에서 사용된 코드보다 더 간결합니다.
삼항 조건 연산자는 두 가지 표현 중 고려할 표현식을 결정하는 효율적인 단축법을 제공합니다.
그러나 과도하게 사용될 경우 가독성이 나빠지니 주의해야 합니다.
Nil 병합 연산자(Nil-Coalescing Operator)
nil 병합 연산자는 a ?? b 형태를 갖는 연산자입니다. 옵셔널 a를 언래핑하여 만약 a가 nil 인 경우 b를 반환합니다. a는 항상 Optional형입니다. b는 "a"의 자료형과 일치해야 합니다.
이 nil 병합 연산자는 다음 코드의 축약형입니다.
a != nil ? a! : b
위의 코드는 삼항 조건 연산자와 강제 언래핑(a!)을 사용하여 a가 nil이 아닐 때 a 안에 래핑 된 값에 접근하고 그렇지 않을 경우 b를 반환합니다.
Nil 병합 연산자는 이 조건부 검사와 언래핑을 간결하고 읽기 쉬운 형태로 캡슐화할 수 있습니다.
a의 값이 nil이 아닌 경우 b의 값은 계산되지 않습니다. 이를 단축 평가(short-circuit evaluation)라고 합니다.
아래 예제에서는 Nil 병합 연산자를 사용하여 기본 색상 이름과 선택적 사용자 정의 색상 이름 중에서 선택합니다.
let defaultColorName = "red"
var userDefinedColorName: String? // defaults to nil
var colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName is nil, so colorNameToUse is set to the default of "red"
userDefinedColorName 변수는 옵셔널 문자열로 정의되며 기본값은 nil입니다.
userDefinedColorName은 옵셔널 타입이기 때문에 Nil 병합 연산자를 사용할 수 있습니다.
위의 예에서 연산자는 colorNameToUse라는 문자열 변수의 초기 값을 결정하는 데 사용됩니다.
userDefinedColorName이 nil이기 때문에 defaultColorName를 반환합니다.
nil이 아닌 값을 userDefinedColorName에 할당하고 nil 병합 연산자 검사를 다시 수행하면 userDefinedColorName에 래핑 된 값이 반환됩니다.
userDefinedColorName = "green"
colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName isn't nil, so colorNameToUse is set to "green"
범위 연산자(Range Operators)
Swift에는 값 범위를 표현하기 위한 단축어인 여러 범위 연산자가 있습니다.
닫힌 범위 연산자(Closed Range Operator)
닫힌 범위 연산자(a...b)는 a에서 b까지 이어지는 범위를 정의하고 a 및 b 값을 포함합니다.
닫힌 범위 연산자는 모든 값을 포함한 범위(예: 루프 포함)에서 유용합니다. 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
반 닫힌 범위 연산자(Half-Open Range Operator)
반 닫힌 범위 연산자(a..<b)는 a에서 b까지의 범위를 정의하지만 b는 포함하지 않습니다.
첫 번째 값을 포함하고 마지막 값은 포함하지 않기 때문에 반 닫힌 범위라고 합니다.
닫힌 범위 연산자와 마찬가지로 a 값은 b보다 커서는 안 됩니다. a 값이 b와 같으면 결과 범위는 비어 있게 됩니다.
반 닫힌 범위 연산자는 Array처럼 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
count의 값은 4지만 "0..< count"는 반 닫힌 범위이기 때문에 0~3입니다.
단방향 범위(One-Sided Ranges)
닫힌 범위 연산자는 한 방향으로 지속되는 범위를 갖습니다. 예를 들어 Index 2부터 배열 끝까지의 요소를 모두 포함합니다.
이 경우 범위 연산자의 한쪽에만 값을 넣어도 됩니다. 연산자의 한쪽에만 값이 있기 때문에 단방향 범위라고 합니다.
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
논리 연산자(Logical Operators)
논리 연산자는 Boolean 값을 true와 false로 수정하거나 결합합니다.
스위프트는 C 기반 언어에서 볼 수 있는 세 가지 표준 논리 연산자를 지원합니다.
Logical NOT (!a)
Logical AND (a && b)
Logical OR (a || b)
논리 부정 연산자(Logical NOT Operator)
논리 부정 연산자(!a)는 부울 값을 반전시켜 참이 거짓이 되고 거짓이 참이 됩니다.
논리 부정 연산자는 접두사 연산자로 공백 없이 연산자가 작동하는 값 바로 앞에 표시됩니다.
let allowedEntry = false
if !allowedEntry {
print("ACCESS DENIED")
}
// Prints "ACCESS DENIED"
if !allowedEntry는 "if not allowed entry."로 읽을 수 있습니다.
후속 행은 "not allowed entry"가 true인 경우, 즉 allowedEntry가 false인 경우에만 실행됩니다.
이 예시처럼 Boolean 상수 및 변수 이름을 신중하게 선택하면 이중 부정이나 혼란스러운 논리문을 피하면서 코드의 가독성을 유지하는 데 도움이 될 수 있습니다.
논리 곱 연산자(Logical AND Operator)
논리 곱 연산자 (a & b)는 전체 식이 true가 되려면 두 값이 모두 true여야 하는 논리 식을 생성합니다.
두 값 중 하나가 거짓이면 전체 식도 거짓이 됩니다.
첫 번째 값이 거짓이면 두 번째 값은 계산되지도 않는데, 이는 전체 식이 참이 될 수 없기 때문이다.
이를 단축 평가(short-circuit evaluation)라고 합니다.
이 예에서는 두 Boolean 값을 고려하며 두 값이 모두 참인 경우에만 액세스를 허용합니다.
let enteredDoorCode = true
let passedRetinaScan = false
if enteredDoorCode && passedRetinaScan {
print("Welcome!")
} else {
print("ACCESS DENIED")
}
// Prints "ACCESS DENIED"
논리 합 연산자(Logical OR Operator)
논리 합 연산자 (a || b)는 두 개의 인접한 파이프 문자로 만들어진 infix 연산자이다.
두 값 중 하나만 참이어도 전체 결과가 참입니다.
위의 논리 곱 연산자와 마찬가지로 논리 합 연산자도 단축 평가를 사용하여 표현식을 고려합니다.
논리 합 연산식의 왼쪽이 참이면 전체 식을 변경할 수 없으므로 오른쪽이 계산되지 않습니다.
아래 예제에서 첫 번째 Boolean 값(hasDoorKey)은 거짓이지만 두 번째 값(knowsOverridePassword)은 참입니다.
하나의 값이 참이기 때문에 전체 식도 true로 평가되고 액세스가 허용됩니다.
let hasDoorKey = false
let knowsOverridePassword = true
if hasDoorKey || knowsOverridePassword {
print("Welcome!")
} else {
print("ACCESS DENIED")
}
// Prints "Welcome!"
논리 연산자의 조합(Combining Logical Operators)
여러 논리 연산자를 결합하여 더 긴 복합 표현식을 만들 수 있습니다.
if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword {
print("Welcome!")
} else {
print("ACCESS DENIED")
}
// Prints "Welcome!"
이 예제는 더 긴 복합식을 만들기 위해 다중 && 및 | 연산자를 사용합니다.
그러나, && 및 || 연산자는 여전히 두 개의 값에서만 작동하므로, 이것은 실제로 서로 연결된 세 개의 작은 표현식입니다.
예제는 다음과 같이 읽을 수 있습니다.
만약 우리가 정확한 도어 코드를 입력하고 망막 스캔을 통과했다면,
혹은 유효한 도어 키가 있거나, 비상 오버라이드 비밀번호를 알고 있다면, 접근을 허용하세요.
entryDoorCode, passedRetinaScan 및 hasDoorKey 값을 기준으로 처음 두 하위 식은 거짓입니다.
그러나 비상 재정의 암호가 알려져 있으므로 전체 복합 식은 여전히 true로 평가됩니다.
스위프트 논리 연산자 && 및 ||는 가장 왼쪽 부분 식을 먼저 평가합니다.
명시적 괄호(Explicit Parentheses)
복잡한 표현식의 의도를 읽기 쉽게 하기 위해 굳이 필요하지 않은 괄호를 쓰기도 합니다.
위의 출입문 예제에서는 복합 표현식의 첫 번째 부분 주위에 괄호를 추가하여 의미를 명확히 할 수 있습니다.
if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword {
print("Welcome!")
} else {
print("ACCESS DENIED")
}
// Prints "Welcome!"
괄호는 처음 두 값이 전체 논리에서 가능한 별도의 상태의 일부로 간주된다는 것을 명확히 합니다.
복합식의 출력은 변하지 않지만, 전체적인 의도는 독자에게 더 명확해집니다.
간결함보다는 가독성을 항상 선호합니다. 의도를 명확히 하는 데 도움이 되는 괄호를 사용하십시오.