23. Expert #1


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:
    bellクラスバインドモードは、一致するターゲットを定数または変数にバインドし、キャビネットブロックで使用します.
    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つの式で複数の外部フォーマット・メンバーにアクセスする方法を学習します.
    記憶.
    1.トーナメントの結果はいつもトーナメント
    2.オフセットフィルタの式で、nilを返す式がある場合は、直後の式ではなくnilを返します.
    var p = Person(name: "Ben", email: "[email protected]")
    let a = p.contacts?.address
    
    var optionalP: Person? = Person(name: "Ben", email: "[email protected]")
    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


    1つのパラメータで2つ以上の値を渡す可変パラメータを学習します.
    (name: Type...)
  • 可変パラメータ(...)...加算すると、可変パラメータ
  • として宣言される
  • パラメータで、2つ以上のセグメントを渡すことができます.
  • 段は配列形式で伝達される.
  • 可変パラメータ各関数は1つの
  • しか使用できません.
  • はデフォルト値を宣言できません.
  • 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


    EscappingとNon Escaptinで実行されるエンクロージャの比較
    Closerパラメータは基本的にnon-servate Closerです
    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


    式をモジュールにカプセル化する@autocrosureプロパティの学習
    // 파라미터로 전달 되는 표현을 클로저로 래핑해줌
    
    // 랜덤 넘버를 리턴하는 단순한 함수
    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 = "[email protected]"
    
    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 = "[email protected]"
    
    let components = email.components(separatedBy: customCharSet)     // 문자열을 분리해주고 리턴함. // .components(separatedBy: )