23. Expert #1

Kang.__.Mingu·2021년 8월 7일
0

1. Study Swift

목록 보기
23/25

Operators

Short-circuit Evaluation, Side Effect

논리 연산자가 논리식을 평가하는 방법에 대해 공부

  • 논리식에서 결과를 도출하는데 필요한 최소한의 코드만 실행하는 것을 [달락평가] 라고 한다.
  • 표현식을 평가 했을 때 값이 변경되거나 상태가 변경되는 것을 사이드 이펙트라고 한다.
  • 논리식에 사이드 이펙트를 발생시킬 수 있는 있는 코드가 포함되어 있으면 논리적인 오류가 발생할 가능성이 높아지기 때문에 조심해야한다.
//flase &&
//true ||

// 달락 평가
var a = 1
var b = 1

func updateLeft() -> Bool {
    a += 1
    return false
}

func updateRight() -> Bool {
    b += 1
    return true
}

let resultA = updateLeft()
let resultB = updateRight()

if resultA && resultB {
    
}
a
b

Operator Methods

기존 연산자가 새로운 형식을 지원하도록 확장 하는 방법에 대해 공부

static func operator(parameters) -> ReturnType {
	statements
}
// 연산자 메소드

"a" == "a"

struct Point {
    var x = 0.0
    var y = 0.0
}

// 개별 속성 비교
extension Point: Equatable {
    static func == (lhs: Point, rhs: Point) -> Bool {
        return (lhs.x == rhs.x) && (lhs.y == rhs.y)
    }
}

let p1 = Point(x: 12, y: 34)
let p2 = Point(x: 67, y: 89)

p1 == p2
p1 != p2

// prefix 키워드로 선언하면 피연산자 앞에 오는 전치연산자로 선언된다
extension Point {
    static prefix func -(pt: Point) -> Point {
        return Point(x: -pt.x, y: -pt.y)
    }
}


let p3 = -p1
p3.x
p3.y

// 증감연산자
// postfix 키워드를 사용하면 피연산자 뒤에오는 후치 연산자로 사용됨
// 후치 증가 연산자 = 현재 값을 리턴한 다음 현재 값을 1씩 증가 시킨다
extension Point {
    static postfix func ++(pt: inout Point) -> Point {
        let ret = pt
        pt.x += 1
        pt.y += 1
        return ret
    }
}

var p4 = Point(x: 1.0, y: 2.0)
let p5 = p4++
p5.x
p5.y

p4.x
p4.y

Custom Operators

Swift가 제공하지 않는 새로운 연산자를 직접 구현하는 방법에 대해 공부

prefix operator operator
postfix operator operator
infix operator operator

Reserved Tokens

(, ), {, }, [, ], ., ,, :, ;, =, @, #, &(prefix operator), ->, `, ?, !(postfix operator), /, /

First Character

/, =, -, +, !, *, %, <, >, &, |, ^, ?, ~

static prefix func operator(parameters) -> ReturnType {
	statements
}
static postfix func operator(parameters) -> ReturnType {
	statements
}
static func operator(parameters) -> ReturnType {
	statements
}

Conditional Statements

Value Binding Pattern

switch 문에서 활용할 수 있는 Value Binding Pattern에 대해 공부

case let name:
case var name:

벨류 바인딩 패턴은 매칭시킬 대상을 상수나 변수로 바인딩한 다음에 케이스 블록에서 활용하는 패턴

let a = 1

switch a {
case var x where x > 100:   // case의 값을 바꾸려면 상수가 아니라 변수로 지정해야된다.
    x = 200
    print(x)
default:
    break
}

// 벨류 바인딩은 튜플에서도 자주 사용
let pt = (1, 2)

switch pt {
case let(x, y):     // 상수로 바인딩 할 때 이 코드가 간단
    print(x, y)
case (let x, let y):
    print(x,y)
case (let x, var y):    // 상수로 바인딩하고 변수로 바인딩 할 때 활용
    print(x,y)
case let(x, _):         // 첫 번째 값만 바인딩(와일드 패턴을 활용)
    print(x)            
}

Expression Pattern

직접 구현한 형식에 대해 패턴 매칭을 적용하는 방법에 대해 공부

let a = 1

switch a {
case 0...10:
    print("0 ~ 10")
default:
    break
}

Pattern Matching Operator

a ~= b

struct Size {
    var width = 0.0
    var height = 0.0
    
    static func ~=(left: Range<Int>, right: Size) -> Bool {
        return left.contains(Int(right.width))
        // 첫 번째 파라미터로 전달 된 범위에 두 번째 파라미터로 전달 된 사이즈에 width의 값이 포함되어 있다면 True가 리턴
    }
}

let s = Size(width: 5, height: 20)

switch s {
case 1..<9:
    print("1 ~ 9")
case 10..<99:
    print("10 ~ 99")
default:
    break
}

Optionals

Optional Chaining

하나의 표현식 내에서 다수의 옵셔널 형식 멤버에 접근하는 방법에 대해 공부

기억
1. 옵셔널 체이닝의 결과는 항상 옵셔널이다
2. 옵셔널 체이닝의 표현식 중에서 하나라도 nil을 리턴한다면 이어지는 표현식을 평가하지 않고 nil을 리턴한다

var p = Person(name: "Ben", email: "swift@example.com")
let a = p.contacts?.address

var optionalP: Person? = Person(name: "Ben", email: "swift@example.com")
let b = optionalP?.contacts?.address


b

optionalP = nil
let c = optionalP?.contacts?.address
c

p.contacts?.address?.count

p.getContacts()?.address

// let f: (() -> Contacts?)? = p.getContacts
// 함수나 메서드에 옵셔널 값이 접근할 떄는 괄호 뒤에 ?를 붙인다.
// f?()?.address


let d = p.getContacts()?.printAddress()

if p.getContacts()?.printAddress() != nil {
    
}


if let _ = p.getContacts()?.printAddress() {
    
}



let e = p.contacts?.email?["home"]


p.contacts?.email?["home"]?.count
// 딕셔너리가 옵셔널로 선언되어 있고 키를 통해 값을 얻을 때는 [] 앞에 ?를 붙인다.
// [] 뒤에 ?를 붙이는 경우는 서브 스크립트에서 속성에 접근하거나 메소드를 호출 할 때



p.contacts?.address = "Daegu"
p.contacts?.address

optionalP?.contacts?.address = "Deagu"
optionalP?.contacts?.address

Optional Pattern

옵셔널 패턴을 활용해서 옵셔널 매칭 코드를 더욱 효율적으로 작성하는 방법에 대해 공부

let a: Int? = 0

let b: Optional<Int> = 0


if let x = a {
   print(x)
}

if case .some(let x) = a {
   print(x)
}


if case let x? = a {
   print(x)
}


let list: [Int?] = [1, nil, nil, 3, nil, 5]

for item in list {
   guard let x = item else { continue }
   print(x)
}


for case let x? in list {
   print(x)
}

// ---------------

//if a == nil {
//
//}
//
//if a == .none {
//
//}

// nil == .none

// 위 아래 둘 다 같은 코드
// ===============

//if a == 1 {
//
//}
//
//if a == .some(1) {
//
//}

// 1 == .some(1)

// 위 아래 같은 코드

Functions

Variadic Parameters

하나의 파라미터를 통해 두 개 이상의 값을 전달하는 가변 파라미터에 대해 공부

(name: Type...)
  • 가변 파라미터(...) ...을 붙이면 가변 파라미터로 선언된다
  • 하나의 파라미터로 두 개 이상의 아그먼트를 전달 할 수 있다.
  • 아그먼트는 배열 형태로 전달 된다.
  • 가변 파라미터는 함수당 하나만 사용 가능하다
  • 기본값 선언이 불가능하다.
print("Hello")

print("Hello", "Swift")

func printSum(of nums: Int...) {
    var sum = 0
    for num in nums {
        sum += num
    }
    print(sum)
}
printSum(of: 1, 2, 3)		// 6

printSum(of: 1, 2, 3, 4, 5)		// 15

Function Types

(ParameterTypes) -> ReturnType
func sayHello() {
    print("Hello, Swift")
}

let f1 = sayHello
f1()


func printHello(with name: String) {
    print("hello, \(name)")
}

let f2: (String) -> () = printHello(with:)

let f3 = printHello(with: )

// 함수에 저장할 때는 아그먼트 레이블을 사용하지 않는다

f3("World")

func add(a: Int, b: Int) -> Int{
    return a + b
}

var f4: (Int, Int) -> Int = add(a: b: )

f4(1,2)



func add(_ a: Int, with b: Int) -> Int {
    return a + b
}

f4 = add(_:with:)

// 입출력
func swapNumbers(_ a: inout Int, _ b: inout Int) {
    
}

let f5 = swapNumbers(_:_:)
f5


// 가변 파라미터

func sum(of numbers: Int...) {
    
}

let f6 = sum(of: )
f6
// 현실적인 코드

func add(_ a: Int, _ b: Int) -> Int {
    return a + b
}

func subtract(_ a: Int, _ b: Int) -> Int {
    return a - b
}

func multiply(_ a: Int, _ b: Int) -> Int {
    return a * b
}

func divide(_ a: Int, _ b: Int) -> Int {
    return a / b
}

typealias ArithmeticFunction = (Int, Int) -> Int

func selectFunction(from op: String) -> ArithmeticFunction? {
    switch op {
    case "+":
        return add(_:_:)
    case "-":
        return subtract(_:_:)
    case "*":
        return multiply(_:_:)
    case "/":
        return divide(_:_:)
    default:
        return nil              // nil을 리턴하려면 반환타입을 옵셔널로 추가해야 됨
    }
}

let af = selectFunction(from: "+")
af?(1, 2)           // 옵셔널 체이닝 활용

selectFunction(from: "*")?(12, 34)

Nested Functions

함수 내부에 새로운 함수를 구현하는 방법에 대해 공부

//다른 함수에 포함되어 있는 함수

func outer() -> () -> () {
    func inner() {              // Nested Functions
        print("inner")
    }
    print("outer")
    
    return inner
}

let f = outer()
f()


//func inner() {
//    print("inner")
//}

outer()

Nonreturning Function

호출하면 코드를 종료하거나 예외를 던지는 특별한 함수를 공부

Nonreturning Function을 호출하면 결과는 함수 Body에서 프로그래밍을 종료하거나 에러를 전달한다

func returnSomething() -> Int {
   return 0
}

let result = returnSomething()
print(result)


func returnNothing() {
   return
}

returnNothing()
print("done")


func doSomethingAndTerminate() -> Never {       // Never = 아무것도 리턴하지 않는다
    fatalError("msg")           // 프로그램 실행을 종료하는 함수
}

// doSomethingAndTerminate()
print("agter terminate")

// 에러를 던지도록 구현
enum MyError: Error {
    case error
}

func doSomethingAndAlwaysThrow() throws -> Never {
    throw MyError.error             // 에러를 던짐
}


//do {
//    try doSomethingAndTerminate()
//    print("after try")
//} catch {
//    print(error)
//}
//
//
//func terminate() -> Never {
//    fatalError("positive only")
//}
//
//func doSomething(with value: Int) -> Int {
//    guard value >= 0 else {
//        terminate()
//    }
//    return 0
//}

// doSomething(with: -1)

Closures

Escaping Closure

Escaping과 Non escaptin 방식으로 클로저를 실행했는데 어떤 차이가 있는지 비교

클로저 파라미터는 기본적으로 non-escaping 클로저이다

func performNonEscapint(closure: () -> ()) {
    print("start")
    closure()
    print("end")
}

// 함수 호출
performNonEscapint {
    print("closure")
}


func performEscaping(closure: @escaping () -> ()) {     // 파라미터가 escaping 클로저로 선언 됨
    print("start")
    
    var a = 12
    
    DispatchQueue.main.asyncAfter(deadline: .now() + 3) {	// 지연시간
        closure()
        print(a)
    }
    print("end")
}

performEscaping {
    print("closure")
}

Autoclosure

아규먼트로 전달된 표현식을 클로저로 래핑하는 @autoclosure 특성에 대해 공부

// 파라미터로 전달 되는 표현을 클로저로 래핑해줌

// 랜덤 넘버를 리턴하는 단순한 함수
func random() -> Int {
   return Int.random(in: 0...100)
}

// 정수를 파라미터로 받아서 print 함수로 출력하는 함수
func takeResult(param: Int) {
   print(#function)
   print(param)
}

takeResult(param: random())
print("-------------------------------")

func takeClosure(param: () -> Int) {
   print(#function)
   print(param())
}

takeClosure(param: { Int.random(in: 0...100) })
print("-------------------------------")


func takeAutoclosure(param: @autoclosure @escaping () -> Int) {       // @autoclosure   // 비동기로 호출하려면 @escaping를 추가해야됨
    // 오토클로저를 호출하면 파라미터를 호출할 수 없음 -> 파라미터를 항상 비워놔야됨
    // 반면 리턴 타입은 원하는 타입으로 선언 할 수 있음
    print(#function)
    DispatchQueue.main.asyncAfter(deadline: .now() + 1 ) {      // 비동기
        print(param())
    }
}

takeAutoclosure(param: Int.random(in: 0...100))     // @autoclosure로 선언되면 클로저로 사용할 수 없음 -> 자동으로 클로저로 래핑되기 때문에

// 오토클로저를 많이 활용하는 것은 어썰트
let rnd = random()
assert(rnd > 30)

String and Character

String Options

메소드를 활용해서 문자열을 편집하는 방법에 대해 공부

Numeric Option

"A" < "B"

"a" < "B"
// 아스키코드 때매 그럼


let file9 = "file9.txt"
let file10 = "file10.txt"

file9 < file10

file9.compare(file10) == .orderedAscending

file9.compare(file10, options: [.numeric]) == .orderedAscending

// [.numeric] 옵션을 사용하면 문자열에 추가된 숫자를 숫자열로 판단한다

Diacritic Insensitive

let a = "Cafe"
let b = "Cafè"

a == b
a.compare(b) == .orderedSame

a.compare(b, options: [.diacriticInsensitive]) == .orderedSame

// [.diacriticInsensitive] = 발음 기호를 무시함

Width Insensitive Option

let a = "\u{30A1}"
let b = "\u{ff67}"

a == b
a.compare(b) == .orderedSame

// 정각 문자와 반각? 문자를 비교하고 싶지 않으면 Width Insensitive Option 추가

a.compare(b, options: [.widthInsensitive]) == .orderedSame

Forced Ordering Option

// 강제로 정렬
let upper = "STRING".lowercased()
let lower = "string"

upper == lower

upper.compare(lower, options: [.caseInsensitive]) == .orderedSame

upper.compare(lower, options: [.caseInsensitive, .forcedOrdering]) == .orderedSame

// [.forcedOrdering] 옵션은 전체 옵션을 적용했을 때 같은 문자열로 판단된다면 일부 옵션을 무시하고 최대한 문자열의 순서를 파악할 수 있는 값을 리턴해줌

Regular Expression

// 정규식 옵션 = 복잡한 패턴의 문자를 쉽게 검색할 수 있음
// 많이 사용함

let emailPattern = "([0-9a-zA-Z_-]+)@([0-9a-zA-Z_-]+)(\\.[0-9a-zA-Z_-]+){1,2}"
let emailAddress = "user@example.com"

if let _ = emailAddress.range(of: emailPattern) {
    print("found")
} else {
    print("not found")
}

// Regular Expression 옵션 추가
if let range = emailAddress.range(of: emailPattern, options: [.regularExpression]), (range.lowerBound, range.upperBound) == (emailAddress.startIndex, emailAddress.endIndex) {
    print("found")
} else {
    print("not found")
}

Character Set

문자 집합
문자열 검색이나 잘못된 문자를 삭제할 때 사용

let a = CharacterSet.uppercaseLetters

let b = a.inverted


var str = "loRem Ipsum"
var charSet = CharacterSet.uppercaseLetters

if let range = str.rangeOfCharacter(from: charSet) {      // 대문자가 검색된다면 첫번째 결과의 범위를 리턴해준다
    print(str.distance(from: str.startIndex, to: range.lowerBound))
}


if let range = str.rangeOfCharacter(from: charSet, options: [.backwards]) {  // [.backwards] 옵션 추가
    print(str.distance(from: str.startIndex, to: range.lowerBound))
}



str = " A p p l e "
charSet = .whitespaces

// .trimmingCharacters 파라미터로 전달 된 캐릭터셋의 포함되어 있는 문자를 문자열에서 삭제한다 그리고 새로운 문자열로 리턴한다
let trimmed = str.trimmingCharacters(in: charSet)
print(trimmed)
// 양끝 공백 삭제됨


var editTarget = CharacterSet.uppercaseLetters

editTarget.insert("#")  // 문자 하나 추가
editTarget.insert(charactersIn: "~!@")      // 문자 여러개 추가

editTarget.remove(charactersIn: "BCD")


// 커스텀 캐릭터셋
let customCharSet = CharacterSet(charactersIn: "@.")    // 두 개의 문자열로 저장된 캐릭터셋 생성
let email = "userId@example.com"

let components = email.components(separatedBy: customCharSet)     // 문자열을 분리해주고 리턴함. // .components(separatedBy: )
profile
최선을 다해 꾸준히 노력하는 개발자 망고입니당 :D

0개의 댓글