✏️

Swift | 연산자 본문

Swift

Swift | 연산자

콩세 2023. 7. 26. 16:29

연산자

- 스위프트의 연산자는 특정한 문자로 표현한 함수이다.

- 특정 연산자의 역할을 프로그래머의 의도대로 변경할 수 있다.

- 연산자에 의해 연상되는 값의 수에 따라 단항, 이항, 삼항 등으로 구분한다.

- 연산자의 위치에 따라 전위, 중위, 후위 등으로 구분한다.

 

 

연산자의 분류

분류 설명
단항 연산자 피연산자(연산 대상)가 한 개인 연산자 ! 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