| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 | 31 |
- 조건문
- pycharm
- mutate()
- input함수
- f-string
- Django
- 점프투장고
- inflearn
- react
- while
- 동일성 연산자
- Swift
- 멤버십 연산자
- 자료형
- 제어문
- sd()
- group_by()
- sqldf
- vs code
- 소수출력
- ReactNative
- summarise()
- COUNT()
- match case
- 반복문
- notion
- 별찍기
- python
- javascript
- R 데이터 분석
- Today
- Total
✏️
Swift | 연산자 본문
연산자
- 스위프트의 연산자는 특정한 문자로 표현한 함수이다.
- 특정 연산자의 역할을 프로그래머의 의도대로 변경할 수 있다.
- 연산자에 의해 연상되는 값의 수에 따라 단항, 이항, 삼항 등으로 구분한다.
- 연산자의 위치에 따라 전위, 중위, 후위 등으로 구분한다.
연산자의 분류
| 분류 | 설명 | 예 |
| 단항 연산자 | 피연산자(연산 대상)가 한 개인 연산자 | ! A |
| 이항 연산자 | 피연산자가 두 개인 연산자 | A + B |
| 삼항 연산자 | 피연산자가 세 개인 연산자 | A ? B : C |
| 전위 연산자 | 연산자가 피연산자 앞에 위치하는 연산자 | ! A |
| 중위 연산자 | 연산자가 피연산자 사이에 위치하는 연산자 | A + B |
| 후위 연산자 | 연산자가 피연산자 뒤에 위치하는 연산자 | A! |
연산자의 종류
1) 할당 연산자 : 값을 할당할 때 사용하는 연산자이다.
| 연산자 | 부호 | 설명 |
| 할당(대입)연산자 | A = B | A에 B의 값을 할당한다. 서로 다른 데이터 타입이라면 오류 발생 |
2) 산술 연산자 : 대체로 수학에서 쓰이는 연산자와 같은 역할을 수행한다.
| 연산자 | 부호 | 설명 |
| 더하기 연산자 | A + B | A와 B를 더한 값을 반환한다. |
| 빼기 연산자 | A - B | A에서 B를 뺀 값을 반환한다. |
| 곱하기 연산자 | A * B | A와 B를 곱한 값을 반환한다 |
| 나누기 연산자 | A / B | A를 B로 나눈 값을 반환한다. |
| 나머지 연산자 | A % B | A를 B로 나눈 나머지를 반환한다. |
* 스위프트의 나머지 연산과 나누기 연산자
스위프트에서는 부동소수점 타입의 나머지 연산까지 지원한다.
기존의 프로그래밍 언어에서는 나머지 연산자가 정수 타입만 지원하는 경우가 많았는데 스위프트에서는 부동소수점 타입도 나머지 연산을 할 수 있다.
ex) let number: Double = 5.0
var result: Double = number.truncatingRemainder(dividingBy: 1.5) // 0.5
result = 12.truncatingRemainder(dividngBy: 2.5) // 2.0
나누기 연산은 기존의 프로그래밍 언어처럼 나머지, 소수점을 제외한 정수만을 결괏값으로 반환한다.
ex) var result:Int = 5 / 3 // 1
result = 10 / 3 // 3
스위프트는 데이터 타입에 엄격함로 서로 다른 자료형끼리의 연산을 엄격히 제한한다.
서로 다른 자료형끼리의 연산을 실행하려면 값을 해당 타입으로 변환한 수 연산해야 한다.
심지어 같은 정수 타입인 Int와 UInt 타입끼리의 연산도 엄격히 제한한다.
3) 비교 연산자 : 두 값을 비교할 때 사용한다.
| 연산자 | 부호 | 설명 |
| 값이 같다 | A == B | A와 B가 같은 값인지 비교하여 불리언 값을 반환한다. |
| 값이 크거나 같다 | A >= B | A가 B보다 크거나 같은 값인지 비교하여 불리언 값을 반환한다. |
| 값이 작거나 같다 | A <= B | A가 B보다 작거나 같은 값인지 비교하여 불리언 값을 반환한다. |
| 값이 크다 | A > B | A가 B보다 큰 값인지 비교하여 불리언 값을 반환한다. |
| 값이 작다 | A < B | A가 B보다 작은 값인지 비교하여 불리언 값을 반환한다. |
| 값이 같지 않다 | A != B | A와 B가 다른 값인지 비교하여 불리언 값을 반환한다. |
| 참조가 같다 | A === B | A와 B가 참조(레퍼런스) 타입일 때 A와 B가 다른 인스턴스를 가리키는지 비교하여 불리언 값을 반환한다. |
| 참조가 같지 않다 | A !== B | A와 B가 참조(레퍼런스) 타입일 때 A와 B가 같지 않은 인스턴스를 가리키는지 비교하여 불리언 값을 반환한다. |
| 패턴 매치 | A ~= B | A와 B의 패턴이 매치되는지 확인하여 불리언 값을 반환한다. |
4) 삼항 조건 연산자 : 피연산자가 3개인 삼항 조건 연산자이다.
Question ? A : B
Question이 참이면 A, 거짓이면 B를 반환한다.
삼항 조건 연산자를 사용하면 조건식을 간단하게 한 줄로 표현할 수 있다.
5) 범위 연산자 : 값(수)의 범위를 나타내고자 할 때 사용한다.
| 연산자 | 부호 | 설명 |
| 폐쇄 범위 연산자 | A...B | A부터 B까지의 수를 묶어 범위를 표현한다. A와 B를 포함한다. |
| 반폐쇄 범위 연산자 | A.. < B | A부터 B미만까지의 수를 묶어 범위를 표현한다. A를 포함하고 B를 포함하지 않는다. |
| 단방향 범위 연산자 | A... | A 이상의 수를 묶어 범위를 표현한다. A를 포함한다. |
| ...A | A 이하의 수를 묶어 범위를 표현한다. A를 포함한다. |
|
| ..< A | A 미만의 수를 묶어 범위를 표현한다. A를 포함하지 않는다. |
6) 부울 연산자 : 불리언 값의 논리 연산을 할 때 사용한다.
| 연산자 | 표현 | 설명 |
| NOT (부정) 부울 연산자 | !B | B(불리언 값)의 참, 거짓을 반전한다. |
| AND 부울 연산자 | A && B | A와 B의 불리언 AND 논리 연산을 실행한다. |
| OR 부울 연산자 | A || B | A와 B의 불리언 OR 논리 연산을 실행한다. |
7) 비트 연산자
| 연산자 | 표현 | 설명 |
| NOT 비트 연산자 | ~ A | A의 비트를 반전한 결과를 반환 |
| AND 비트 연산자 | A & B | A와 B의 비트 AND 논리 연산을 실행 |
| OR 비트 연산자 | A | B | A와 B의 비트 OR 논리 연산을 실행 |
| XOR 비트 연산자 | A ^ B | A와 B의 XOR 논리 연산을 실행 |
| 비트 이동 연산자(시프트 연산자) | A >> B A << B |
A의 비트를 B만큼 시프트(이동)한다. |
시프트 연산자는 지정하는 수만큼 피연산자를 왼쪽(<<) 또는 오른쪽(>>)으로 이동한다.
8) 복합 할당 연산자 : 할당 연산자와 다른 연산자가 하는 일을 한 번에 할 수 있도록 연산자를 결합한 것
| 표현 | 설명 | 같은표현 |
| A += B | A와 B의 합을 A에 할당한다 | A = A + B |
| A -= B | A와 B의 차를 A에 할당한다 | A = A - B |
| A *= B | A와 B의 곱을 A에 할당한다 | A = A * B |
| A /= B | A를 B로 나눈 값을 A에 할당한다 | A = A / B |
| A %= B | A를 B로 나눈 나머지를 A에 할당한다. | A = A % B |
| A <<= N | A를 N만큼 왼쪽 비트 시프트 한 값을 A에 할당한다 | A = A << N |
| A >>= N | A를 N만큼 오른쪽 비트 시프트 한 값을 A에 할당한다 | A = A >> N |
| A &= B | A와 B의 비트 AND 연산 결과를 A에 할당한다 | A = A & B |
| A != B | A와 B의 비트 OR 연산 결과를 A에 할당한다 | A = A | B |
| A ^= B | A와 B의 비트 XOR 연산 결과를 A에 할당한다. | A = A ^ B |
9) 오버플로 연산자
기존 프로그래밍 언어에서는 오버플로(또는 언더플로) 가능성이 있는 연산에 대해서 따로 오버플로에 대한 추가 알고리즘 및 로직 등을 설계하는 것이 일반적이었음.
스위프트는 기본 연산자를 통해 오버플로에 대비할 수 있게 준비해둔다.
오버플로 연산자를 사용하면 오버플로를 자동으로 처리한다.
| 연산자 | 부호 | 설명 |
| 오버플로 더하기 연산 | &+ | 오버플로에 대비한 덧셈 연산을 한다 |
| 오버플로 빼기 연산 | &- | 오버플로에 대비한 뺄셈 연산을 한다 |
| 오버플로 곱하기 연산 | &* | 오버플로에 대비한 곱셈 연산을 한다 |
10) 기타 연산자 : 이외에 스위프트 라이브러리에 기본적으로 정의된 연산자
| 연산자 | 부호 | 설명 |
| nil 병합 연산자 | A ?? B | A가 nil이 아니면 A를 반환하고, A가 nil이면 B를 반환한다 |
| 부호변경 연산자 | -A | A(수)의 부호를 변경한다. |
| 옵셔널 강제 추출 연산자 | O! | O(옵셔널 개체)의 값을 강제로 추출한다 |
| 옵셔널 연산자 | V? | V(옵셔널 값)을 안전하게 추출하거나, V(데이터 타입)가 옵셔널임을 표현한다. |
연산자 우선순위와 결합 방향
- 스위프트에서는 연산자 우선순위를 지정해 두었기 떄문에 헷갈릴 때 확인하면 된다.
- 우선순위가 높은 연산자는 자신에 비해 우선순위가 낮은 연산자보다 먼저 실행된다.
- 프로그래머가 임의로 정의하는 사용자 정의 연산자 또한 이 규칙에 따라 실행 순서가 결정된다.
- 연산자가 연산하는 결합방향도 지정되어 있다.
- 결합방향은 같은 우선순위에 있는 연산자끼리 나열되었을 때 어느 방향부터 그룹지을 것인지 나타낸다.
- 기본 연산자들의 우선도와 결합방향을 알아보려면 스위프트 표준 라이브러리의 연산자 정의를 참고하면 된다.
ex) 스위프트 연산자 정의 확인
infix operator === : ComparisonPrecedence
infix operator ~= : ComparisonPrecedence
infix operator &= : AssignmentPrecedence
...
(infix키워드로 선언되어 있다 = 중위 연산자를 뜻함)
- 위 예시에서 연산자 우선도와 결합 방향을 알 수 없음
- 연산자 뒤에 콜론을 붙이고 이어서 써준 연사낮 우선순위 그룹을 지정해준 것이기 때문이다.
- 스위프트 표준 라이브러리에는 다양한 우선순위 그룹이 존재한다.
ex) 스위프트 표준 연산자 우선순위 그룹
precedencegroup BitwiseShiftPrecedence {
higherThan: MultiplicationPrecedence
} ...
- 위 예시에서 보았듯 연산자 우선순위 그룹은 higherThan, lowerThan, associativity 등으로 우선순위 및 결합 방향을 지정한다.
- 연산자 우선순위는 절대치가 아닌, 상대적인 수치이다.
스위프트 표준 라이브러리의 연산자 우선순위 그룹 우선순위별 정렬(우선순위 높은 순으로..)
- 연산자 우선순위가 높을수록 같은 라인의 연산자 중 먼저 처리된다.
| 연산자 우선순위 그룹 이름 | 결합 방향 | 할당 방향 사용 |
| DefaultPrecedence | none | false |
| BitwiseShiftPrecedence | none | false |
| MultiplicationPrecedence | left | false |
| AdditionPrecedence | left | false |
| RangeFormationPrecedence | none | false |
| CastingPrecedence | none | false |
| NilCoalescingPrecedence | right | false |
| ComparisonPrecedence | none | false |
| LogicalConjunctionPrecedence | left | false |
| LogicalDisjunctionPrecedence | left | false |
| TernaryPrecedence | right | false |
| AssignmentPrecedence | right | true |
| FunctionArrowPrecedence | right | false |
ex) 연산자 우선순위에 따른 처리순서
let inValue: Int = 1
let resultValue1: Int = intValue << 3 + 5 // 8 + 5 => 13
resultValue1은 1(intValue)을 3만큼 왼쪽으로 이동(비트연산자<<)하고 +5를 해준 값을 뜻한다.
1 << 3 은 정수 1을 이진수 변환한 비트 값을 왼쪽으로 3칸 이동하라는 뜻이다.
정수 1을 이진수로 변환하면 1이고, 1을 3칸 이동시키면 1000이 된다.
이진수 1000을 십진수로 변환하면 8이 된다.
<<연산은 BitwiseShiftPrecedence 연산자 우선순위 그룹을 우선순위로 가지고,
+연산은 AdditionPrecedence 연산자 우선순위 그룹을 우선순위로 가진다.
따라서 << 연산이 +연산보다 더 먼저 실행된다.
let resultValue2: Int = 1 * 3 + 5 // 3 + 5 => 8
*연산은 MultiplicationPrecedence 연산자 우선순위 그룹을 우선순위로 가지므로 +연산보다 먼저 실행된다.
사용자 정의 연산자
- 스위프트에서는 사용자의 입맛에 맞게 연산자 역할을 부여할 수 있다.
- 기존에 존재하지 않던 연산자 기호를 만들어 추가할 수도 있다.
- 기존 연산자의 역할을 변경하거나 새로운 역할을 추가하기 위해서는 기존의 연산자가 전위, 중위, 후위 연산자인지 알아야 한다.
전위 연산자 : 연산자가 피연산자 앞에 위치하는 연산자. : prefix- 대표적인 예로 부울 부정 논리연산(NOT) 연산자(!)가 있다.ex) !A
중위 연산자 : 피연산자 사이에 위치하는 연산자. 많은 연산자가 여기에 속한다. : infixex) A + B
후위 연산자 : 피연산자 뒤에 위치하는 연산자 : postfix- 대표적인 예로 옵셔널 강제 추출 연산자 등이 있다.ex) 0!
- operator키워드는 연산자임을 뜻하고, associativity는 연산자 결합방향을 뜻한다.- precedence는 우선순위를 뜻한다.- 사용자 정의 연산자는 아스키 문자 ( /, =, -, +, !, *, %, <, >, &, |, ^, ?, ~)를 결합해 사용한다.- 마침표(.)를 사용자 정의 연산자에 사용할 수 있다.- 마침표를 사용할때는 문자 중 맨 처음의 문자가 마침표일 때만 연산자에 포함된 마침표가 연산자로 인식된다.
전위 연산자 정의와 구현- Int 타입의 제곱을 구하는 연산자로 **을 전위 연산자로 사용하려고 한다.- 기존에 없는 전위 연산자를 만들고 싶다면 연산자 정의를 해야한다. prefix operator **- 위처럼 연산자 정의를 마치면, 어떤 데이터 타입에 이 연산자가 동작할 것인지 함수를 구현한다.- 전위 연산자 함수를 구현할 때는 함수 func 키워드 앞에 prefix 키워드를 추가한다.ex) 전위 연산자 구현과 사용 prefix operator ** prefix func ** (value: Int) -> Int { return value * value }
// func키워드로 **연산자가 같은 값을 곱해주는 (제곱) 함수임을 정의한 것
let minusFive: Int = -5 let sqrtMinusFive: Int = **minusFive print(sqrtMinusFive) // 25
기존에 존재하는 전위 연산자 중 정수에 사용되는 느낌표(!)를 문자열에 사용하려고 한다.문자열 앞에 사용하면 문자열이 비어있는지 확인하는 연산자로 사용할 것이다.ex) prefix func ! (value: String) -> Bool { return value.isEmpty } var stringValue: String = "sebin" var isEmptyString: Bool = !stringValue print(isEmptyString) // false : 문자열이 있으면 false
stringValue = " " isEmptyString = !stringValue print(isEmptyString) // true : 문자열이 없으면 true
후위 연산자 정의와 구현
- 정수 타입의 값 뒤에 **을 붙이면 10을 더해주는 연산을 구해보겠다.
- 후위 연산자의 구현 함수 앞에는 postfix라는 키워드를 붙인다
ex) 사용자 정의 후위 연산자 정의와 함수 표현
postfix operator **
postfix func ** (value: Int) -> Int {
return value + 10
}
let five: Int = 5
let fivePlusTen: Int = five**
print(fivePlusTen) // 15
- 전위 연산자와 후위 연산자를 한 번에 사용하게 되면 후위 연산을 먼저 실행한다.
ex) 전위 연산자와 후위 연산자의 동시 사용
prefix operator **
postfix operator **
prefix func ** (value: Int) -> Int {
return value * value
}
postfix func ** (value: Int) -> Int {
return value + 10
}
let five: Int = 5
let sqrtFivePlusTen: Int = **five**
print(sqrtFivePlusTen) // (10 + 5) * (10 + 5) == 225
중위 연산자 정의와 구현
- 중위 연산자 정의도 전위, 후위와 비슷하나, 우선순위 그룹을 명시해 줄 수 있는게 다르다.
- 연산자 우선순위 그룹은 precedencegroup 뒤에 그룹 이름을 써주어 정의할 수 있다.
ex) precedencegroup 우선순위 그룹 이름 {
higherThan: 더 낮은 우선순위 그룹 이름
lowerThan: 더 높은 우선순위 그룹 이름
associativity: 결합방향(left/right/none) // 생략시 default: none
assignment: 할당방향 사용(true/false)
}
associativity : 결합방향을 명시해줄 수 있다.
- 결합방향이 있는 더하기 또는 빼기 연산자는 1+2+3과 같이 연산할 수 있고, 3-2-1과 같이 연산할 수 있다.
- 결합방향이 있는 연산자는 섞어서 1+2-3처럼도 사용할 수 있다.
- 결합방향이 없는 부등호 연산자는 연달아 사용할 수 없다.
- 1<2<3과 같이 사용할 수 없다는 것.
assignment : 옵셔널 체이닝과 관련된 사항.
**을 중위 연산자로 사용하기 위해 정의해보자.
ex) 중위 연산자의 정의
infix operator ** : MultiplicationPrecedence
// :MultiplicationPrecedence가 없다면 DefaultPrecedence 그룹으로 자동 지정된다.
import Foundation // String 타입의 contains(_:) 메서드를 사용하기 위해 Foundation 프레임워크를 임프트한다.
infix operator ** : MultiplicationPrecedence
fun ** (Ihs: String, rhs: String) -> Bool {
return Ihs.contains(rhs)
}
let helloSebin: String = "Hello sebin"
let sebin: String = "sebin"
let isContainsSebin: Bool = helloSebin ** sebin // true
우리가 정의한 데이터 타입(클래스, 구조체 등)에서 유용하게 사용할 수 있는 연산자도 새로 정의하거나 중복 정의할 수 있다.
ex) 클래스 및 구조체의 비교 연산자 구현
class Car {
var modelYear: Int? // 연식
var modelName: String? // 모델 이름
}
struct SmartPhone {
var company: String? // 제조사
var model: String? // 모델
}
// Car 클래스의 인스턴스끼리 == 연산했을 때 modelName이 같다면 true를 반환
func == (lhs: Car, rhs: Car) -> Bool {
return lhs.modelName == rhs.modelName
}
// SmartPhone 구조체의 인스턴스끼리 == 연산했을 때 model이 같다면 true를 반환
func == (lhs: SmartPhone, rhs: SmartPhone) -> Bool {
return lhs.model == rhs.modelName
}
- 위 코드를 타입 내부에 타입 메서드로 구현할 수 있다.
ex) 타입 메서드로 구현된 사용자 정의 비교 연산자
class Car {
var modelYear: Int? // 연식
var modelName: String? // 모델 이름
// Car 클래스의 인스턴스끼리 == 연산했을 때 modelName이 같다면 true를 반환
static func == (lhs: Car, rhs: Car) -> Bool {
return lhs.modelName === rhs.modelName
}
}
'Swift' 카테고리의 다른 글
| Swift | 데이터 타입 고급 (0) | 2023.07.24 |
|---|---|
| Swift | 데이터 타입 기본 (0) | 2023.07.24 |
| Swift | 기본 명명 규칙 (0) | 2023.07.24 |