[Swift] Advanced Operators

41765 단어 swiftiOSreviewTILTIL

Bitwise Operators : 비트

데이터 구조 내에서 데이터를 비트 단위로 조작할 수 있는 연산자

  • 큰 데이터를 사용하는 그래픽, 영상 데이터를 조작 할때 유용하다.
  • 장치 드라이버 생성과 같은 낮은 수준의 프로그래밍에도 활용이 가능하다.
  • 사용자 지정 프로토콜을 통한 통신을 위한 인코딩 및 디코딩 데이터와 같은 외부 소스의 원시 데이터로 작업할 때도 유용할 수 있다.

Bitwise NOT Operator

  • ~A : A 의 비트를 반전한 결과를 반환한다.
  • 숫자의 모든 비트를 반전한 결과를 반환한다.

    let initialBits: UInt8 = 0b00001111
    let invertedBits = ~initialBits  // equals 11110000

Bitwise AND Operator

  • A & B : A 와 B 의 비트 AND 논리 연산을 실행한다.
  • 두 숫자의 비트를 결합한다. 비트가 두 입력 번호 모두에서
    1과 같은 경우에만 비트가 1로 설정된 새 숫자를 반환한다.

    let firstSixBits: UInt8 = 0b11111100
	let lastSixBits: UInt8  = 0b00111111
	let middleFourBits = firstSixBits & lastSixBits  
    // equals 00111100
  • firstSixBitslastSixBits의 값은 모두 1과 같은 네 개의 중간 비트를 가지고 있다.
  • 비트 AND 연산자는 그것들을 결합하여 같은 숫자 00111100을 만든다.

Bitwise OR Operator

  • A|B : A 와 B 의 비트 OR 논리 연산을 실행한다.
  • 두 숫자의 비트를 비교한다.
  • 비트가 입력 번호에서 1과 같으면 비트가 1로 설정된 새 숫자를 반환한다.

  	let someBits: UInt8 = 0b10110010
	let moreBits: UInt8 = 0b01011110
	let combinedbits = someBits | moreBits  
    // equals 11111110
  • someBitsmoreBits의 값은 다른 비트를 1로 설정한다.
  • 비트 OR 연산자는 그것들을 결합하여 같은 숫자 11111110을 만든다.

Bitwise XOR Operator = Exclusive OR operator

  • A ^ B : A 와 B 의 비트 XOR 논리 연산을 실행한다.
  • 두 숫자의 비트를 비교하지만, 비트가 1로 설정된 새로운 숫자를 반환하며 입력 비트는 다른 0으로 설정되며 입력 비트는 동일하다.

  	let firstBits: UInt8 = 0b00010100
	let otherBits: UInt8 = 0b00000101
	let outputBits = firstBits ^ otherBits  
    // equals 00010001
  • firstBitsotherBits의 값은 각각 다른 비트가 그렇지 않은 위치에서 1로 설정된다.
  • 비트 XOR 연산자는 이 두 비트를 출력 값에서 1로 설정한다.
  • firstBitsotherBits의 다른 모든 비트는 출력 값에서 0으로 설정된다.

Bitwise Left and Right Shift Operators

  • A >> B A << B : A 의 비트를 B 만큼 비트를 이동한다.
  • 정의된 규칙에 따라 숫자의 모든 비트를 특정 수의 장소에 의해 왼쪽이나 오른쪽으로 이동한다.
  • 좌우 이동은 정수를 2배로 곱하거나 나누는 효과가 있다.
  • 정수의 비트를 한 위치로 왼쪽으로 이동하면 값이 두 배가 되는 반면, 한 위치로 오른쪽으로 이동하면 값이 절반으로 줄어든다.

Shifting Behavior for Unsigned Integers : 서명되지 않은 동작의 이동

  • 부호 없는 정수에 대한 비트 이동 동작은 다음과 같다.
  • 기존 비트는 요청된 장소 수에 따라 왼쪽이나 오른쪽으로 이동한다.
  • 정수 저장소의 경계를 넘어 움직이는 모든 비트는 폐기된다.
  • 0은 원래 비트가 왼쪽이나 오른쪽으로 이동한 후 뒤에 있는 공간에 삽입된다.

  • 11111111 << 1 (1111111111이 왼쪽으로 이동)과 11111111 >> 1 (11111111이 오른쪽으로 이동)의 결과를 보여준다.
  • 파란색 숫자는 이동되고, 회색 숫자는 버려지고, 분홍색 0이 삽입된다.
   let shiftBits: UInt8 = 4   // 00000100 in binary
   shiftBits << 1             // 00001000
   shiftBits << 2             // 00010000
   shiftBits << 5             // 10000000
   shiftBits << 6             // 00000000
   shiftBits >> 2             // 00000001

Shifting Behavior for Signed Integers : 서명 된 동작의 이동

  • 이동 동작은 부호 있는 정수가 이진수로 표현되는 방식 때문에 부호 없는 정수보다
    서명된 정수에 더 복잡하다.
  • 서명된 정수는 정수가 양수인지 음수인지 나타내기 위해
    첫 번째 비트(기호 비트라고도 함)를 사용한다.
  • 0의 기호 비트는 양수, 1의 기호 비트는 음수임을 의미합니다.
  • 나머지 비트(값 비트라고도 함)는 실제 값을 저장한다.
    양수는 부호 없는 정수와 정확히 같은 방식으로 저장되며, 0에서 위쪽으로 계산된다.

  • 음수 비트를 양수처럼 왼쪽과 오른쪽으로 이동할 수 있으며, 왼쪽으로의 모든 교대마다 두 배로 늘리거나 오른쪽으로 전환할 때마다 절반으로 줄일 수 있다.
  • 이를 달성하기 위해, 서명된 정수가 오른쪽으로 이동할 때 추가 규칙이 사용된다. 서명된 정수를 오른쪽으로 이동할 때, 부호 없는 정수와 동일한 규칙을 적용하지만,
    왼쪽의 빈 비트를 0이 아닌 기호 비트로 채워야한다.

  • 양수와 음수가 저장되는 특별한 방식 때문에, 둘 중 하나를 오른쪽으로 이동하면 0에 가까워진다.
    이 교대 동안 기호 비트를 동일하게 유지한다는 것은 값이 0에 가까워지면서 음수 정수가 음수로 유지된다는 것을 의미한다.

Overflow Operators = Underflow

overflow 연산자를 이용하면 overflow를 자동으로 처리할 수 있다.

  • 그 값을 보유할 수 없는 정수 상수나 변수에 숫자를 삽입하려고 하면,
    기본적으로 Swift는 잘못된 값을 생성하기보다는 오류를 보고한다.
  • overflow너무 크거나 너무 작은 숫자로 작업할 때 추가적인 안전을 제공한다. 경계 값 조건을 코딩할 때 훨씬 더 많은 유연성을 가질 수 있다.

    var potentialOverflow = Int16.max
    // potentialOverflow equals 32767, which is the maximum value an Int16 can hold
    potentialOverflow += 1
    // this causes an error
  • -3276832767 사이의 서명된 정수를 저장할 수 있다.
    이 범위를 벗어난 숫자로 설정하려고 하면 오류가 발생한다.
  • Swift는 정수 계산을 위한 오버플로 동작을 선택하는 세 개의 산술 오버플로 연산자를 제공한다.
    • 오버플로 추가 `(&+)
    • 오버플로 뺄셈 (&-)
    • 오버플로 곱셈 (&*)

     var unsignedOverflow = UInt8.max
    // unsignedOverflow equals 255, which is the maximum value a UInt8 can hold
    unsignedOverflow = unsignedOverflow &+ 1
    // unsignedOverflow is now equal to 0
  • unsignedOverflow 변수는 UInt8이 보유할 수 있는 최대 값(255 또는 11111111)으로 초기화된다.
  • 그런 다음 추가 연산자(&+)를 사용하여 1씩 증가한다.
    이것은 UInt8이 보유할 수 있는 크기 바로 위에 이진 표현을 밀어내어 아래 다이어그램과 같이 경계를 넘어 넘치게 된다.
  • 오버플로 추가 후 UInt8의 범위 내에 남아있는 값은 00000000 또는 0이 된다.

  • 부호 없는 정수가 음수 방향으로 overflow할 수 있을 때 비슷한 일이 일어난다.

       var unsignedOverflow = UInt8.min
    // unsignedOverflow equals 0, which is the minimum value a UInt8 can hold
    unsignedOverflow = unsignedOverflow &- 1
    // unsignedOverflow is now equal to 255
  • UInt8이 보유할 수 있는 최소값은 0 또는 바이너리로 00000000이다.
  • 뺄셈 연산자(&-)를 사용하여 00000000에서 1을 빼면,
    숫자가 넘쳐나고 11111111 또는 소수점 255가 된다.

  • overflow는 부호 있는 정수에 대해서도 발생한다.
  • 부호 있는 정수에 대한 모든 덧셈과 뺄셈은 비트 방식으로 수행되며,
    부호 비트를 더하거나 빼는 숫자의 일부로 포함되어 있다.

      var signedOverflow = Int8.min
      // signedOverflow equals -128, which is the minimum value an Int8 can hold
      signedOverflow = signedOverflow &- 1
      // signedOverflow is now equal to 127
  • Int8이 보유할 수 있는 최소값은 -128 또는 10000000이다.
  • overflow1을 빼면 01111111의 값이 주어지며,
    이는 기호 비트를 전환하고 Int8이 보유할 수 있는 최대 양의 값인
    127을 제공한다.

  • 부호 있는 정수와 부호 없는 정수 모두에 대해,
    양수 방향의 오버플로우는 최대 유효한 정수 값에서 최소값으로 감싸고, 음수 방향의 오버플로우최소값에서 최대값으로 감싼다.

      var unsignedInteger: UInt8 = 0
      let errorUnderflowResult: UInt8 = unsignedInteger - 1
      // 런타임 오류
      let underflowedValue: UInt8 = unsignedInteger &- 1 
      // 255
      
      unsignedInteger = UInt8.max // 255
      let errorOverflowresult: UInt8 = unsignedInteger + 1
      // 런타임 오류
      let overflowedValue: UInt8 = unsignedInteger & +1
      // 0 
      

Precedence and Associativity : 우선순위와 연관성

  • Swift 는 연산자 우선순위(Precedence)를 지정해놓았기 때문에
    코딩하다가 헷갈리는 경우 확인하면 된다.
  • 우선순위가 높은 연산자는 낮은 연산자보다 먼저 실행된다.
  • Swift에서 C와 마찬가지로 나머지 연산자(%)와 곱셈 연산자(*)
    덧셈 연산자(+)보다 우선 순위가 높다. 나머지와 곱셈은 서로 같은 우선순위를 가지고 있다.
  • 사용할 정확한 평가 순서를 작성하려면, 그들의 연관성을 고려해야한다.
    나머지와 곱셈은 모두 왼쪽의 표현과 관련이 있다.
  • 프로그래머가 임의로 정의하는 사용자 정의 연산자(Custom Operators)도 이 규칙에 따른다.
  • 연관성 즉, 결합방향(Associativity)은 같은 우선순위에 있는 연산자끼리 나열되었을 때 어느 방향부터 그룹지을 것인지 나타낸다.
	1+2+3+4 ➡️ (((1+2)+3)+4) 
  • 연산자 + 은 모두 같은 우선도이며 결합방향은 왼쪽이기때문에
    (((1+2)+3)+4)왼쪽부터 그룹이 묶이며. 1+2가 가장 먼저 연산이 되어 3 이 되고
    그 결과가 다시 4와 연산이 된다.
  • 만약 결합방향이 오른쪽이라면 (1+(2+(3+4)))처럼 묶여서 연산이 된다.

Custom Operators : 사용자 정의

  • Swift에서 제공하는 표준 연산자 외에도 자신만의 사용자 지정 연산자를 선언하고 구현할 수 있다.
  • 기존에 존재하지 않던 연산자 기호를 만들어 추가할 수 있다.
  • BUT 할당 연산자(=) 와 삼항 연산자(?:) 는 사용자 정의 역할을 부여할 수 없다.
  • 사용자 정의 연산자는 아스키(ASCH)문자 /, =, -, +, !, *, %, <,>,&, ^, ?, ~ 를 결합해서 사용한다.
  • 마침표(.) 는 연산자를 표현하는 문자 중 맨 처음의 문자가 마침표( .+. )일때만 연산자에 포함된 마침표가 연산자로 인식된다.
  • 전위연산자는 물음표(?) , 느낌표 (!) 로 시작하는 사용자 정의 연산자로 정의할 수 없다.

Prefix : 전위연산자

prefix: 전위연산자!A 연산자가 피연산자 앞에 위치하는 연산자. 대표적인 예로 부울부정논리연산(NOT), 연산자(!)가 있다.

  • 기존에 없던 전위 연산자를 만들고 싶다면 연산자 정의를 먼저 해주어야한다.
  • 정의한다 : '이제 이 연산자를 사용하겠다'는 뜻.
  • 전위연산자 함수를 구현할때는 함수 func 키워드 앞에 prefix 키워드를 추가해준다.
    		prefix operator **
       
       prefix func ** (value: Int) -> Int {
       	return value * value
       }
       
       let minusFive: Int = -5
       let sqrtMinusFive: Int = **minusFive
       
       print(sqrtMinuFive) // 25
  • 전위연산자에 기능을 추가할 때는 함수만 중복정의(**)하면 된다.
  • 느낌표(!) 를 문자열 앞에 사용하게 되면 문자열이 비어있는지 확인하는
    연산자로 사용하기 위해 함수를 중복 정의해준다.
prefix func ! (value: String) -> Bool {
   	return value.isEmpty
   }
   
 var stringValue: String = "yagom"
 var isEmptyString : Bool = !StringValue
   
 print(isEmptyString) // false
   
 stringValue = ""
 isEmptyString = !stringValue
   
 print(isEmptyString) // true
   
  • ** 전위 연산자를 String 타입에서도 동작할 수 있도록 중복정의할 수 있다.

    prefix operator **
     
     prefix func ** (value: String -> String {
    	return value + " " + value }
     
     let resultString: String = **"yagom"
     
     print(resultString)  // yagom yagom
      
     

Infix : 중위연산자

Infix :중위연산자A+B 피연산자 사이에 위치하는 연산자.
많은 수의 연산자가 여기 속하며 우선순위 그룹을 명시해줄 수 있다.

  • 연산자 우선그룹 그룹은 중위 연산자에서만 사용된다.
  • 전위 연산자 및 후위 연산자는 결합방향 및 우선순위를 지정하지 않는다.
  • 연산자 우선순위 그룹은 precedenegroup 뒤에 그룹이름을 써주어 정의할 수 있다.
  • 만약 우선순위 그룹을 명시해주지 않으면
    우선순위가 가장 높은 Defaultprecedenegroup 그룹을 우선순위 그룹으로 갖게된다.
      precedenegroup 우선순위 그룹 이름 {
          higherthan: 더 낮은 우선순위 그룹 이름
          lowerThan: 더 높은 우선순위 그룹 이름
          associativity: 결합방향(left / right / none) 
          assignment: 할당방향 사용(true / false)
          }
  • higherThan , lowerThan : 새로 만들어 줄 우선순위 그룹과의 상화관계를 설정해 줄 수 있다. lowerThan 은 현재 정의된 우선순위 그룹만 명시할 수 있다.
  • associativity : 빼고 그룹을 정의하면 기본적으로 none이 설정된다.
  • 결합방향이 없는 연산자(<) 는 여러 번 연달아 사용할 수 없다.
  • 1 > 2 > 3 연달아 사용할 수 없다는 뜻.
  • assignment : Optional Chaining을 포함한 연산에 있을 경우 연산자의 우선순위를 지정한다.
    • true 일 경우 : 오른쪽부터 체이닝이 시작된다.
    • false 혹은 assignment 를 따로 명시해주지 않을 경우 :
      할당을 하지 않는 연산자와 같은 Optional Chaining 규칙을 따른다.

  • 문자열과 문자열 사이에 ** 연산자를 사용하면 뒤에 오는 문자열이 앞의 문자열 안에
    속해있는지 확인하는 연산을 실행할 수 있다.
    // String 타입의 contains(_:) 메서드를 사용하기 위햐 Foundation 프레임워크를 임포트한다.
    import Foundation

    infix operator **  : MultiplicationPrecedence

    func ** (1hs: String, rhs:String) -> Bool {
        return 1hs.contains(rhs)
    }

    let helloYagom: String = "Hello yagom"
    let yagom: String = "yagom"
    let isContainYagom: Bool = helloYagom ** yagom // true
  • 데이터타입(클래스, 구조체 등)에서 유용하게 사용할 수 있는 연산자도
    새로 정의하거나 중복정의할 수 있음을 알 수 있다.
class Car {
	var modelYear: Int? // 연식
    var modelName: String? // 모델 이름 
 }   
struct Smartphone {
	var company: String? // 제조사
    var model: String? // 모델
}

// Car클래스의 인스턴스끼리 == 연산했을 때 modelName이 같다면 True를 반환
func == (1hs: Car, rhs: Car) -> Bool {
	return 1hs.modelName == rhs.modelName
 }
 
// smartphone 구조체의 인스턴스끼리 == 연산했을 때 model이 같다면 True를 반환
func == (1hs: smartphone, rhs: smartphone) -> Bool {
	return 1hs.model == rhs.model
 }
 
 let myCar = Car()
 myCar.modelName = "S"
 
 let yourCar = Car()
 yourCar.modelName = "S"
 
var myPhone = SmartPhone()
myPhone.model = "SE"

var yourPhone = SmartPhone()
yourPhone.model = "6"

print(myCar == yourCar) // true
print(myPhone == yourPhone) // false

  • 특정 타입에 국한된 연산자 함수라면 그 타입 내부에 구현되는 것이 읽고 이해하기에 더 쉽기에
    타입 내부에 타입 메서드로 구현이 가능하다.
  • 타입 메서드로 구현한 사용자 정의 연산자는 각 타입의 익스텐션으로도 구현할 수 있다.
class Car {
	var modelYear: Int? // 연식
    var modelName: String? // 모델 이름 
 }
 // Car클래스의 인스턴스끼리 == 연산했을 때 modelName이 같다면 True를 반환
 static func == (1hs: Car, rhs: Car) -> Bool {
	return 1hs.modelName == rhs.modelName
 }
 
 
struct Smartphone {
	var company: String? // 제조사
    var model: String? // 모델
}
 
// smartphone 구조체의 인스턴스끼리 == 연산했을 때 model이 같다면 True를 반환
func == (1hs: smartphone, rhs: smartphone) -> Bool {
	return 1hs.model == rhs.model

Postfix : 후위연산자

postfix: 후위연산자0!피연산자 뒤에 위치하는 연산자.
옵셔널 강제 추출 연산자 등이 있다.

  • 전위 연산자와 다르지 않다.
      postfix operator **

      postfix func ** (value: Int) -> Int {
          return value + 10 
          }

      let five: Int = 5
      let fivePlusTen: Int = five**

      print(fivePlusTen) // 15
  • 하나의 피연산자에 전위 연산과 후위 연산을 한 줄에 사용하게 되면 후위 연산을 먼저 수행한다.
	  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 fivePlusTen: Int = **five**

      print(sqrtfivePlusTen) // (10 + 5 ) * ( 10 + 5 ) == 225 

기타 연산자

💡 Nil-Coalescing Operator (nil 병합 연산자) :
a != nil ? a! : b 의 삼항 조건부 연산자를 A ?? B로 줄인 것.
간결하고 읽기 쉬운 형태로 만들 수 있다.
A가 nil이 아니면 A를 반환하고, A가 nil이면 B를 반환한다.
옵셔널을 사용할 때 아주 유용한 연산자.

let valueInt: Int = someOptionalInt  != nil! ? someOptionalInt! : 0

// 위 코드와 같은 결과를 볼 수 있지만 훨씬 안전하게 옵셔널을 다룰 수 있다.
let valueInt: Int = someOptionalInt ?? 0
  • 부호변경 연산자 : -A A(수)의 부호를 변경한다.
  • 옵셔널 강제 추출 연산자 : O! O(옵셔널 개체)의 값을 강제로 추출한다.
  • 옵셔널 연산자 : V? V(옵셔널 값)를 안전하게 추출하거나 데이터타입이 옵셔널임을 표현한다.

🔗 참고자료

[Swift] 고급 연산자 Part 1 - 비트연산자들1 Bitwise Operators1
https://blog.naver.com/badwin/221178028123

[Swift] 고급 연산자 Part 2 - 비트연산자들2 Bitwise Operators2 시프트연산 Shift Operators, 연산자 우선순위,
https://blog.naver.com/badwin/221180694420

부호
https://namu.wiki/w/부호

스위프트 오버플로우 연산
https://hyerios.tistory.com/175

[iOS / Swift] Swift 문법을 알아보자! : 11편 : 옵셔널 체이닝 (Optional Chaining)
https://velog.io/@wook4506/iOS-Swift-옵셔널-체이닝-Optional-Chaining

Operator Declarations | Apple Developer Documentation
https://developer.apple.com/documentation/swift/swift_standard_library/operator_declarations

Advanced Operators — The Swift Programming Language (Swift 5.6)
https://docs.swift.org/swift-book/LanguageGuide/AdvancedOperators.html

야곰 Swift - 옵셔널 체이닝과 nil 병합 연산자
https://youtu.be/UANvklNnDeg

📕 야곰의 스위프트 프로그래밍 3판 p.120-136

좋은 웹페이지 즐겨찾기