연산자는 다음과 같이 값을 확인하거나 변화, 합치기 위해 사용되는 특별한 상징이나 구문을 의미합니다.
let i = 1 + 2
if enteredDoorCode && passedRetinaScan
Swift 의 연산자들은 비정상적인 결과값을 갖지 않도록 연산의 결과가 오버플로우 하는지를 감지하는데, 오버플로우 행동과 관련된 연산자를 활용하고 싶다면 Overflow Operators 를 확인해주세요.
Swift 는 C 언어에서는 제공되지 않았던 a..<b
나 a...b
와 같은 줄임표현도 제공합니다. 더 많은 연산자들을 확인하고 싶다면 Advanced Operators 를 확인해주세요.
연산자는 크게 단항 (unary), 이항 (binary), 삼항 (ternary) 으로 구분할 수 있습니다.
-a
와 같이 1개의 목표 대상을 지닌 연산자를 의미하며, 접두연산자 prefix, 앞에 붙는 연산자, !a
와 접미연산자 postfix, 뒤에 붙는 연산자, a!
로 구분할 수 있습니다.2 + 3
과 같이 2개의 목표 대상을 지닌 연산자를 의미하며 중위 연산자 (infix) 라고도 불립니다.a ? b : c
목표 대상은 피연산자 (operand) 로 지칭합니다.
1 + 2
에서 +
는 이항 연산자, 중위 연산자 (infix) 이며 1
과 2
는 피연산자 (operand) 입니다.
할당 연산자 (a = b)
는 값을 초기화하거나 새롭게 할당할 때 사용합니다.
let b = 10
var a = 5
a = b
// a 의 값은 이제 10 입니다.
만약 오른쪽의 값이 튜플과 같이 여러개의 값을 갖고 있다면 한번에 나누어서 값을 할당할 수 있습니다.
let (x,y) = (1,2)
// x 는 1 이며, y 는 이제 2 입니다.
C 언어와 다르게 Swift 의 할당연산자 =
는 스스로 값을 리턴하지 않습니다. 따라서 다음과 같은 구문은 사용할 수 없습니다.
if x = y {
code ....
code ....
}
더하기 +
, ex) 1 + 2
더하기는 String 타입에서도 사용할 수 있습니다.
ex) "Hello", + "World!"
빼기 -
, ex) 5 - 3
곱하기 *
, ex) 2 * 3
나누기 /
, 10.0 / 2.5
나머지 연산자 a % b
는 b 가 a에 근접하도록 최대한의 곱셈을 해준 후, 남은 값을 반환하는 연산자입니다.
다른 언어에서는 나머지 연산자를 "modulo" 연산자로 알려져있습니다, 만
Swift 의 나머지 연산자는 음의 나머지 값을 반환하므로,
엄밀히 말해서, "remainder" 연산자라고 부르는 것이 맞습니다.
번역 :
a = -9, b = 4 일 때,
% 를 modulo 연산을 하게 되면
4 * 3 을 한 후 남은 3이 나머지 값이 되지만
% 를 remainder 로 계산하게 되면
4 * 2 를 한 후 남은 -1 이 나머지 값이 됩니다.
Swift 는 % 계산을 하면 -1 이 남으므로
용어는 remainder 가 맞다라고 "엄밀히" 말하고 있습니다.
정수는 접두연산 -
를 사용해서 양의 값을 음으로 바꿀 수 있습니다.
let three = 3
let minusThree = -three // minusThree == -3
let plusThree = -minusThree // plusThree == 3
이처럼 빼기 -
를 값의 바로 앞에 붙임으로써 해당 값이 연산되기 이전에 값을 바꾸도록 사용할 수 있습니다.
더하기도 똑같이 할 수 있습니다. 만 아무런 영향도 미치지 않습니다.
C 언어처럼 Swift 는 =
과 다른 연산자를 합친 복합 연산자를 지원합니다.
var a = 1
a += 2
Note : 단 복합연산자는 값을 리턴하지 않습니다. 따라서 다음과 같은 구문은 사용할 수 없습니다.
let b = a += 2
Swift standard library 가 제공하는 연산자에 대한 정보를 확인하고 싶으시다면 Operator Declarations 를 확인해주세요.
Swift 는 다음과 같은 비교 연산을 지원합니다.
a == b
a != b
a > b
a < b
a >= b
a <= b
비교 연산자는 if 구문에서 자주 사용됩니다. if 구문에 대해 더 알고 싶다면 Control Flow 를 확인해주세요.
비교 연산자는 같은 자료형과 같은 갯수를 가진 튜플(tuple) 에도 사용할 수 있습니다. 튜플을 비교할 때는 왼쪽에서 오른쪽으로, 한번에 한번씩만 비교하며 두 값이 다를 때 까지만 진행됩니다.
(1, "zebra") < (2, "apple")
// true, 1이 2보다 작기 때문입니다. zebra 와 apple 은 비교되지 않습니다.
(3, "apple") < (3, "bird")
// ture, 3과 3이 같고 apple 이 bird 보다 작기 때문입니다.
// 번역 : String 은 길이가 아닌 서로의 앞 글자부터 아스키코드 값을 비교합니다.
(4, "dog") == (4, "dog")
// true, 4과 4가 같고 "dog" 가 "dog" 로 같기 때문입니다.
위의 예시와 같이 비교 연은 왼쪽에서 오른쪽으로 진행되는 것을 확인할 수 있습니다. 첫번째 예시를 보면 "zebra" 와 "apple" 이 비교되지 않는 것을 확인할 수 있는데, 비교 연산이 이미 첫번째로 비교한 1
2
비교에서 끝났기 때문입니다. 만약 서로의 첫번째 원소가 같다면 두번째 원소로 이동하여 서로를 비교하게 됩니다.
튜플끼리의 비교는 각 튜플 원소들의 값을 비교할 수 있을 때만 적용할 수 있습니다. 예를 들어 아래의 예시와 같이 (String, Int) 튜플은 비교할 수 있지만 (String, Bool) 은 비교할 수 없습니다. Bool 은 <
, >
, ==
로 비교할 수 없기 때문입니다.
("blue", -1) < ("purple", 1) // 비교 가능하며 true 입니다.
("blue", false) < ("purple", true) // 비교 자체가 불가능합니다.
Note: Swift standard library 에서 튜플은 7개 미만의 원소만 비교할 수 있습니다. 7개 이상의 원소를 포함한 튜플을 비교하고 싶다면 직접 비교 연산을 적용시켜야 합니다.
삼항 연산자는 question ? answer1 : answer2
, 3 부분으로 이루어진 특별한 연산을 수행합니다. question
이 true
라면 answer1
을, false
라면 answer2
를 리턴합니다.
삼항 연산자는 다음의 줄임 표현입니다.
question? answer1 : answer2
if question {
answer1
} else {
answer2
}
침대의 가로길이를 맞추려고 할 때, 침대가 머리맡 선반이 있다면 50을, 없다면 20을 주고 싶은 상황이라면 다음과 같이 코드를 작성할 수 있습니다.
let contentHeight = 40
let hasHeader = true
let rowHeight = contentHeight + (hasHeader ? 50 : 20)
-----------------------------------------------------
let contentHeight = 40
let hasHeader = true
let rowHeight: Int
if hasHeader {
rowHeight = contentHeight + 50
} else {
rowHeight = contentHeight + 20
}
삼항연산자는 효율적으로 표현을 줄일 수 있습니다. 하지만 남용하면 지나친 간결성이 코드의 가독성을 저해하므로 여러개의 상황을 결합하여 삼항 연산자를 사용하지 않도록 합니다.
닐 병합연산자 a ?? b
(이하 병합연산자) 는 옵셔널(optional) 을 꺼내 풀어서 값을 갖고 있다면 (a 가 nil 이 아니라면) 그 값을, 값이 nil 이라면 기본 값인 b 를 리턴하는 연산을 의미합니다. 이때 a
는 항상 옵셔널(optional) 자료형이어야 하며, b
는 항상 a 의 자료형과 일치해야 합니다.
병합연산자는 다음의 줄임 표현이라고 할 수 있습니다.
a != nil ? a! b
이 코드는 삼항 연산자를 통해 a!
를 수행하고, 만약 a 의 값이 nil 이라면 b 를 리턴하고 있습니다.
Note: 만약 a 가 옵셔널 자료형이 아니라면 b 는 수행되지 않습니다. short-circuited 에 준하는 내용입니다.
병합 연산자의 예시로, 기본색과 유저가 정한 색을 고르는 상황입니다.
let defaultColorName = "red"
var userDefinedColorName: String? // nil 이 기본값입니다.
var colorNameToUse = uerDefinedColorName ?? defaultColorName
// userDefinedColorName 이 nil 이라면
// colorNameToUse 에는 defaultColorName 이 할당됩니다.
userDefinedColorName
변수는 옵셔널 String 으로 정의되어 있기에, 병합연산자를 통해서 값을 정해줄 수 있습니다.
만약 userDefinedColorName
이 nil 이 아니라면 defaultColorName
대신 userDefinedColorName
이 사용됩니다.
userDefinedColorName = "green"
colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName 이 nil 이 아니기때문에, colorNameToUse 는 "green" 으로 할당됩니다.
Swift 는 범위를 짧게 표현할 수 있는 여러 범위 연산자가 있습니다.
닫힌 범위 연산자는 (a...b)
로 a 부터 b 까지 범위에 a 와 b 를 모두 함하는 연산을 의미합니다. 이때 a
는 반드시 b
보다 작아야합니다.
닫힌 범위 연산자는 for ... - in ...
반복문처럼 모든 값을 확인하고 싶을 때 유용합니다.
for index in 1...5 {
print("\(index) 곱하기 5 는 \(index * 5)")
}
// 1 곱하기 5 는 5
// 2 곱하기 5 는 10
// 3 곱하기 5 는 15
// 4 곱하기 5 는 20
// 5 곱하기 5 는 25
더 자세한 for-in
반복문은 Control Flow 를 확인해주세요.
반열림 범위 연산자 (a..<b)
는 a 부터 b 까지의 범위 중 b 를 포함하지 않는 연산을 의미합니다. 반열림의 의미는 처음의 값은 포함하지만 마지막을 포함하지 않기 때문입니다. 닫힘 범위 연산자가 그러했듯 a
는 b
보다 작아야합니다. 만약 a
와 b
가 같다면 해당 범위 연산자는 빈 값을 리턴합니다.
반열림 범위 연산자는 특히 0을 기준으로한 배열과 함께 사용할 때 유용합니다.
let names = ["안나", "알렉스", "브라이언", "뉴원"]
let count = names.count
for i in 0..<count {
print("사람\(i+1) 은 \(naems[i])라고 불립니다.")
}
// 사람1 안나라고 불립니다.
// 사람2 알렉스라고 불립니다.
// 사람3 브라이언라고 불립니다.
// 사람4 뉴원라고 불립니다.
위에서 반열림 범위 연산자는 배열이 4개의 원소를 갖고 있지만, 0..<count
가 3, 배열의 마지막 인덱스, 까지만 호출하고 있습니다. 배열에 대해 더 자세히 알고 싶다면 Arrays를 참고해주세요.
닫힌 범위 연산자는 한 방향으로 계속해서 반복하는 형태의 연산자를 가질 수 있습니다. 이를테면 범위는 2부터 배열의 끝까지 가도록 할 수 있습니다. 이때 특정 값을 범위 연산자로부터 꺼내 사용할 수 있게됩니다. 이런 종류의 연산자를 한방향 범위 연산자(One-Sided Ranges) 라고 합니다. 연산자가 한쪽 방향에만 있기 때문에 이러한 이름이 붙게 되었습니다.
for name in names[2...] {
print(name)
}
// 브라이언
// 뉴원
for name in names[...2] {
print(name)
}
// 안나
// 알렉스
// 브라이언
반열림 범위 연산자 역시 위와 동일한 맥락에서 사용할 수 있습니다.
for name in names[..<2] {
print(name)
}
// 안나
// 알렉스
한방향 범위 연산자는 for-in
구문에서만 사용할 수 있는 것이 아닌, 다른 상황에서도 사용할 수 있습니다. 하지만 이때 한방향 범위 연산자는 범위가 정해져있지 않는 연산자에게서 값을 지정할 수 없게됩니다. 범위가 정해져있지 않기에 그 값이 명확하지 않기 때문입니다.
let range = ...5
range.contains(7) // false
range.contains(5) // true
range.contains(-1) // true
논리 연산자는 Boolean 자료형의 논리 값을 true 나 false 로 수정하거나 병합하는 연산자를 의미합니다. Swift 는 C 언어 바탕의 3개의 논리 연산자를 제공합니다.
NOT 연산자, 부정 연산자 !a
는 Boolean 자료형의 값을 뒤집는 것으로 true
가 false
로, false
가 true
로 전환되는 것을 의미합니다.
부정 연산자는 접미 연산자로, 해당 값이 연산되기 이전에 나타나며 "a 가 아니다 (not a)" 로 읽을 수 있습니다.
let allowedEntry = false
if !allowedEntry {
print("ACCESS DENIED")
}
// "ACCESS DENIED" 가 출력됩니다.
부정 연산자를 활용함으로써 코드의 가독성과 간결성을 유지할 수 있으며, 부정형을 두번 사용하거나 혼란스러운 논리 기재를 피할 수 있습니다.
AND 연산자, 논리곱 연산자 a && b
는 두 값이 모두 true 임을 통해 전체 표현또한 true
임을 나타내는 연산자입니다.
만약 두 값이 false
라면 전체 표현 역시 false
가 됩니다. 이때, 사실 첫번째 값이 false
라면 두번째 값은 계산조차 되지 않는데 왜냐하면 이미 전체 표현식은 true
가 될 수 없기 때문입니다.
let enteredDoorCode = true
let passedRetinaScan = false
if enteredDoorCode && passedRetinaScan {
print("Welcome!")
} else {
print()"ACCESS DENIED
}
// "ACCESS DENIED" 가 출력됩니다.
OR 연산자, 논리합 연산자 a || b
는 중위 연산자로 파이프 모양의 Character 가 2개 있는 것으로 만들 수 있습니다. OR 연산자는 둘 중 하나의 값이라도 true
라면 전체 표현 역시 true
임을 나타내는 연산자입니다.
OR 연산자도 AND 연산자와 마찬가지로, 첫번째 값이 true
라면 두번째 값부터는 계산조차 되지 않는데 왜냐하면 이미 전체 표현식은 false
가 될 수 없기 때문입니다.
let hasDoorKey = false
let knowsOverridePassword = true
if hasDoorKey || knowsOverridePassword {
print("Welcome!")
} else {
print("ACCESS DENIED")
}
// "Welcome" 이 출력됩니다.
논리 연산자들을 여러개 사용하여 더욱 길고, 조합된 표현식을 사용할 수도 있습니다.
if enteredDoorCode && passRetinaScan || hasDoorKey || knowsOverridePassword {
print("Welcome!")
} else {
print("ACCESS DENIED")
}
// "Welcome" 이 출력됩니다.
위의 코드는 길어보이지만 3개의 논리 연산을 진행하고 있습니다.
enteredDoorCode 와 passRetinaScan 가 true 인지
,
hasDoorKey 가 true 인지
knowsOverridePassword 가 true 인지
입니다.
Note: Swift 의 논리연산자는 왼쪽이 우선순위에 있습니다. 즉, 다수의 논리 연산자를 사용하더라도, 내부에서 나뉘더라도 왼쪽부터 논리 연산을 진행합니다.
위의 코드에서 괄호를 통해 코드를 명백하게 할 수 있습니다.
if (enteredDoorCode && passRetinaScan) || hasDoorKey || knowsOverridePassword {
print("Welcome!")
} else {
print("ACCESS DENIED")
}
// "Welcome" 이 출력됩니다.
괄호가 연산에 영향을 미치지는 않습니만, 코드의 가독성을 위해 활용할 수 있습니다.