본문 바로가기

Swift 언어 가이드

Swift 언어 가이드 - 기본 연산자

*Swift Language Guide를 우리말로 풀이하고 편집한 것이다. Basic Operators 섹션의 내용을 담고 있다.

 

용어 설명

연산자는 피연산자의 개수에 따라 단항 연산자, 이항 연산자, 삼항 연산자로 나뉜다.

단항 연산자(Unary Operator)

`-a`처럼 하나의 피연산자를 대상으로 하는 연산자를 단항 연산자라 한다. 단항 연산자는 `!b` 같이 대상 앞에 오는 전위 연산자(Prefix operator)와 `c!` 같이 대상 뒤에 오는 후위 연산자(Postfix operator)로 나뉜다.

이항 연산자(Binary Operator)

`2 + 3`처럼 두 개의 피연산자를 대상으로 하는 연산자를 이항 연산자라 한다. 이항 연산자는 두 개의 피연산자 사이에 있기 때문에 중위 연산자(Infix operator)라고도 한다.

삼항 연산자(Ternary Operator)

세 개의 피연산자를 대상으로 하는 연산자를 이항 연산자라 한다. C 언어와 마찬가지로 Swift의 삼항 연산자는 `a ? b : c`와 같은 형식의 조건 연산자 하나뿐이다.

피연산자(Operand)

연산자가 연산을 수행하는 대상을 피연산자라 한다. `1 + 2`라는 식에서 `+` 기호는 이항 연산자이고, `1`과 `2`라는 값은 피연산자이다.

 


대입 연산자

대입 연산자(Assignment operator) `=`는 우항을 좌항에 대입하는 역할을 한다. 

 

let b = 10
var a = 5
a = b
// a의 값은 10이다

 

우항이 여러 개의 값으로 이루어진 튜플인 경우, 좌항에 상수나 변수를 입력하여 튜플의 요소를 할당할 수 있다. 

 

let (x, y) = (1, 2)
// x는 1, y는 2이다

 

C와 Objective-C의 대입 연산자와 달리 Swift의 대입 연산자는 값을 반환하지 않는다. 이는 같음 연산자(`==`)를 사용해야 할 자리에 대입 연산자(`=`)를 사용하는 것을 방지하기 위함이다. `if x = y`는 유효하지 않은 코드이기 때문에 오류를 보다 쉽게 수정할 수 있다.

 

if x = y {
    // x = y는 값을 반환하지 않기 때문에 유효하지 않은 코드가 된다. 
}

 


산술 연산자

Swift는 모든 숫자 타입에 다음과 같은 4개의 표준 산술 연산자(Arithmetic operator)를 지원한다.

- 더하기 `+`

- 빼기 `-`

- 곱하기 `*`

- 나누기 `/`

 

var a = 1 + 2       // a 값은 3
var b = 5 - 3       // b 값은 2
var c = 2 * 3       // c 값은 6
var d = 10.0 / 2.5  // d 값은 4.0

 

C와 Objective-C의 산술 연산자와 다르게 Swift의 산술 연산자는 기본적으로 오버플로를 허용하지 않는다. 

 

더하기 연산자(`+`)는 스트링을 연결할 때에도 사용할 수 있다.

 

print("hello, " + "world")  // "hello, world" 출력

나머지 연산자

나머지 연산자(Remainder operator) `%`는 피제수를 제수로 나누었을 때 나누어 떨어지지 않고 남는 수를 구하는 역할을 한다. 

 

var a = 9 % 4    // a 값은 1

 

나머지 연산자는 양수와 음수 모두에 적용되는데, 이때 좌항의 값을 기준으로 하고 우항의 부호는 무시한다. `a % -b`는 `a % b`와 값이 같다.

 

var a = 9 % 4  // a 값은 1
var b = 9 % -4  // b 값은 1
var c = -9 % 4  // c 값은 -1
var d = -9 % -4  // d 값은 -1

단항 빼기 연산자

단항 빼기 연산자(Unary minus operator) `-`를 공백 없이 값 앞에 붙여 부호를 전환할 수 있다. 이미 음수인 값에 `-`를 붙이면 양수가 된다.

 

let three = 3
let minusThree = -three       // minusThree의 값은 -3
let plusThree = -minusThree   // plusThree의 값은 3

단항 더하기 연산자

단항 더하기 연산자(Unary plus operator) `+`는 특별한 기능 없이 기존 값을 그대로 반환한다. 단항 더하기 연산자는 음수에 단항 빼기 연산자를 사용할 때 코드의 대칭을 맞추는 용도로 양수에 사용할 수 있다.

 

let minusSix = -6
let alsoMinusSix = +minusSix  // alsoMinusSix의 값은 -6

 


복합 대입 연산자

복합 대입 연산자(Compound assignment operator)는 대입 연산자(`=`)와 다른 연산자를 결합한 것이다. 대표적인 예로 더하기 대입 연산자(`+=`)를 들 수 있다. 이는 대입과 더하기를 동시에 수행하는 연산자로, `a += b`와 같은 형태로 사용되며 `a = a + b`라는 뜻을 가진다. 

 

var a = 1
a += 2
// a의 값은 3

 

복합 대입 연산자는 값을 반환할 수 없기 때문에 `let b = a += 2`와 같은 코드도 사용할 수 없다.

 


비교 연산자

Swift는 다음과 같은 표준 비교 연산자(Comparison operator)를 지원한다.

- 같음 `a == b`

- 같지 않음 `a != b`

- 큼 `a > b`

- 작음 `a < b`

- 크거나 같음 `a >= b`

- 작거나 같음 `a <= b`

 

모든 비교 연산자는 해당 비교문이 참인지 아닌지를 나타내는 Bool 값을 반환한다. 

 

1 == 1   // 값은 true 
2 != 1   // 값은 true 
2 > 1    // 값은 true 
1 < 2    // 값은 true 
1 >= 1   // 값은 true 
2 <= 1   // 값은 false 

 

비교 연산자는 if 문 같은 조건문에 주로 사용된다. 

 

let name = "world"
if name == "world" {
    print("hello, world")
} else {
    print("I'm sorry \(name), but I don't recognize you")
}

// name의 값은 world이므로 "hello, world"가 출력된다

 

튜플의 경우, 요소의 타입과 개수가 동일하다면 비교 연산자를 활용하여 두 튜플을 비교할 수 있다. 왼쪽에서 오른쪽 순서로 비교가 이루어진다. 첫 번째 시도에서 비교 결과를 도출할 수 있는 경우 비교가 종료되고, 첫 번째 요소가 동일한 경우에는 두 번째 요소로 넘어간다.

 

(1, "zebra") < (2, "apple") 
// 1이 2보다 작기에 true 반환. zebra와 apple은 비교하지 않음.

(3, "apple") < (3, "bird")
// 3과 3은 동등하기에 apple과 bird를 비교하여 true 반환.

(4, "dog") == (4, "dog")
// 4와 4는 동등하고 dog와 dog도 동등하기에 true 반환.

 

튜플 비교는 연산자를 튜플 요소에 적용할 수 있는 경우에 한해서만 가능하다. 연산자 `<`로 String이나 Int 값은 비교할 수 있지만 Bool 값을 비교할 수는 없다. 

 

("blue", -1) < ("purple", 1)        // 비교 가능
("blue", false) < ("purple", true)  // 비교 불가능

 


삼항 조건 연산자

삼항 조건 연산자(Ternary conditional operator)는 세 개의 영역으로 이루어진 특수 연산자로, `question ? answer1 : answer2`와 같은 형태를 띈다. 조건(`question`)의 참/거짓 여부에 따라 두 값(`answer1`과 `answer2`) 중 하나를 선택하라는 명령이다. `question`이 참일 경우 `answer1`을 반환하고 거짓일 경우에는 `answer2`를 반환한다. 이를 상세하게 표현하면 다음과 같다.

 

if question {
    answer1
} else {
    answer2
}

 

표의 행 높이를 구하는 경우를 예로 들어 보자. 행에 헤더가 있을 경우 행 높이는 콘텐츠 높이보다 50포인트 더 높아야 하고, 헤더가 없을 경우에는 20포인트 더 높아야 한다. 이를 코드로 나타내면 다음과 같다. 

 

let contentHeight = 40
let hasHeader = true
let rowHeight = contentHeight + (hasHeader ? 50 : 20)
// rowHeight의 값은 90이다

 

위 코드를 보다 이해하기 쉽게 풀어 쓰면 다음과 같다. 

 

let contentHeight = 40
let hasHeader = true
let rowHeight: Int
if hasHeader {
    rowHeight = contentHeight + 50
} else {
    rowHeight = contentHeight + 20
}

 

삼항 조건 연산자를 사용하면 rowHeight의 값을 단 한 줄의 코드로 설정할 수 있다. 하지만 삼항 조건 연산자는 코드를 간결하게 하는 것만큼 이해하기 어렵게 만들 수도 있기 때문에 유의해서 사용해야 하며, 하나의 복합 문 안에 여러 개의 삼항 조건 연산자를 사용하는 것은 지양해야 한다. 

 


nil 병합 연산자

nil 병합 연산자(nil-coalescing operator) `??`는 옵셔널 값이 nil일 때 적용할 기본값을 설정하기 위해 사용한다. `a ?? b`와 같은 형태로 사용하며 옵셔널 a의 값이 있는 경우 옵셔널을 해제하고 값이 nil인 경우 기본값 b를 반환한다는 의미이다. `a`는 반드시 옵셔널이어야 하며 `b`의 타입과 `a`의 타입이 일치해야 한다. 이 코드를 삼항 조건 연사자를 이용하여 풀어 쓰면 다음과 같다.

 

a != nil ? a! : b
// a가 nil이 아니면 a를 해제한 값을 반환하고 a가 nil이면 b를 반환한다

 

a 값이 nil이 아니면 옵셔널 a를 해제하고 b는 평가되지 않는다. 이를 단락 평가(short-circuit evaluation)라 한다.

 

보다 구체적인 예를 들어 살펴보자. 별도로 색상 이름을 지정하지 않으면 기본 색상 이름이 적용되는 것을 nil 병합 연산자로 나타내면 다음과 같다.

 

let defaultColorName = "red"
var userDefinedColorName: String?   // userDefinedColorName의 값은 nil

var colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName의 값이 nil이기 때문에 defaultColorName이 colorNameToUse에 할당된다. colorNameToUse의 값은 "red"이다.

 


범위 연산자

범위 연산자는 값의 범위를 표현하는 데 사용된다. 

닫힌 범위 연산자(Closed range operator)

닫힌 범위 연산자 `...`는 하한값과 상한값을 모두 포함하는 범위를 지정하는 연산자이다. `a...b`와 같이 연산자 앞에 하한값(`a`)을 입력하고 연산자 뒤에 상한값(`b`)을 입력한다. `a...b`는 `a`와 `b` 사이의 모든 값을 포함하는 범위를 나타내며, `a`와 `b`도 그 범위에 포함된다. 닫힌 범위 연산자는 `for-in`과 같은 반복을 통해 범위 내의 모든 값을 사용할 때 유용하다.

 

for index in 1...5 {
    print("\(index) times 5 is \(index * 5)")
}

반 개방 범위 연산자(Half-open range operator)

반 개방 범위 연산자 `..<`는 하한값만 포함하는 범위를 지정하는 연산자이다. `a..<b`는 `a`부터 `b`까지의 모든 값을 포함하되 b는 제외하는 범위를 나타낸다. `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])")
}
// 전체 개수는 4개이지만 인덱스 번호는 3까지 있기 때문에 반 개방 범위를 사용한다

단방향 범위(One-sided range)

연산자 앞뒤에 하한값이나 상한값을 생략하여 한쪽 범위만 명시한 것을 단방향 범위라 한다. 단방향 범위는 사용하는 연산자의 종류에 따라 닫힌 범위가 될 수도 있고 반 개방 범위가 될 수도 있다. `...b`는 b보다 작은 값과 b를 모두 포함하는 범위를 나타내고, `..<b`는 b보다 작은 값만 포함하는 범위를 나타낸다. 

 

하한값이 생략된 단방향 범위에서는 시작점이 없기 때문에 반복을 실행할 수 없지만, 상한값이 생략된 범위에서는 무한정 반복이 가능하기 때문에 반복을 실행할 수 있다. 다만 실제로 반복이 무한정 실행되지 않게 반복의 종료 조건을 명시해 주어야 한다. 더불어 단방향 범위는 해당 범위 안에 특정 값이 있는지 확인하는 용도로 사용할 수도 있다. 이를 코드로 나타내면 다음과 같다. 

 

let range = ...5
range.contains(7)   // false
range.contains(4)   // true
range.contains(-1)  // true

 


논리 연산자

논리 연산자(Logical operator)는 불리언 논리값(true, false)을 수정하거나 결합하는 역할을 한다. Swift의 논리 연산자에는 NOT 연산자(`!`), AND 연산자(`&&`), OR 연산자(`||`)가 있다. 

NOT 연산자 

전위 연산자로서 피연산자 앞에 위치하는 NOT 연산자 `!`는 불리언 값을 반전하는 역할을 한다(true는 false로, false는 true로). `!a`와 같은 형태로 사용되며 이는 "`a`가 아님"으로 해석된다. `a` 값이 true인 경우 `!a`의 값은 false이며, `a` 값이 false인 경우 `!a`의 값은 true가 된다. 다음과 같이 활용할 수 있다.

 

let allowedEntry = false
if !allowedEntry {
    print("ACCESS DENIED")
}

// if !allowedEntry는 allowedEntry의 반대가 true인 경우, 즉 allowedEntry가 false인 경우를 나타낸다

AND 연산자

중위 연산자로서 두 개의 피연산자 사이에 위치하는 AND 연산자 `&&`는 두 피연산자의 값이 모두 true여야 전체 식의 값이 true가 되는 논리 식을 만든다. 피연산자의 값이 하나라도 false인 경우, 전체 값은 false가 된다. 단락 평가에 따라 첫 번째 피연산자의 값이 false인 경우 전체 값은 false로 평가되고 두 번째 피연산자에 대한 평가를 실시하지 않는다. AND 연산자는 다음과 같이 활용할 수 있다.

 

let enteredDoorCode = true
let passedRetinaScan = false
if enteredDoorCode && passedRetinaScan {
    print("Welcome!")
} else {
    print("ACCESS DENIED")
}

OR 연산자

두 개의 파이프 문자(`||`)로 이루어진 OR 연산자는 두 개의 피연산자 사이에 위치하는 중위 연산자로, 두 피연산자의 값 중 하나만 true여도 전체 식의 값이 true가 되는 논리 식을 만든다. OR 연산자도 AND 연산자처럼 단락 평가를 통해 식을 평가한다. 왼쪽 피연산자의 값이 true이면 오른쪽 피연산자의 값에 관계없이 전체 식의 값이 결정된다. OR 연산자는 다음과 같이 활용할 수 있다. 

 

let hasDoorKey = false
let knowsOverridePassword = true
if hasDoorKey || knowsOverridePassword {
    print("Welcome!")
} else {
    print("ACCESS DENIED")
}

논리 연산자 결합하기

여러 개의 논리 연산자를 결합하여 복합 식을 만들 수 있다.

 

let enteredDoorCode = true
let passedRetinaScan = false
let hasDoorKey = false
let knowsOverridePassword = true

if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword {
    print("Welcome!")
} else {
    print("ACCESS DENIED")
}

 

위 코드에서 if 조건은 세 개의 식으로 구성되어 있다. 이 조건이 참이 되기 위해서는 enteredDoorCode와 passedRetinaScan이 모두 참이거나 hasDoorKey가 참이거나 knowsOverridePassword가 참이어야 한다. enteredDoorCode && passedRetinaScan과 hasDoorKey의 값은 false이지만 knowsOverridePassword의 값이 true이므로 "Welcome!"이 출력된다.

이해를 돕기 위해 표시하는 괄호

코드 실행을 위해 꼭 필요한 것은 아니지만 복잡하게 구성되어 있는 식을 보다 이해하기 쉽게 만들기 위해 괄호를 추가할 수 있다. 위에서 사용한 예시 코드에 다음과 같이 괄호를 추가하면 `(enteredDoorCode && passedRetinaScan)`이 세 조건 중 하나라는 것이 보다 명확해진다.

 

let enteredDoorCode = true
let passedRetinaScan = false
let hasDoorKey = false
let knowsOverridePassword = true

if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword {
    print("Welcome!")
} else {
    print("ACCESS DENIED")
}

 

간결함보다 중요한 것은 가독성이다. 코드 의도를 명확히 하는 데 도움을 줄 수 있다면 괄호를 사용하는 것이 좋다.

'Swift 언어 가이드' 카테고리의 다른 글

Swift 언어 가이드 - 문자와 문자열  (0) 2020.05.05
Swift 언어 가이드 - 기초  (0) 2020.04.02