Skip to content

Pattern matching with ~= operator #4

@hyunable

Description

@hyunable

Pattern matching with ~= operator

패턴매칭에 숨겨진 operator 인 ~= 의 개념과 오버로드를 통한 적용사례들을 다룹니다.

conception

swift는 특정 패턴을 검사할 수 있는 패턴 매칭 문법을 제공합니다.
패턴 매칭이란 어떤 변수 또는 조건식이 만족하는 경우의 수에 따라 다르게 동작하도록 하는 것을 말한다.

주로 쓰이는 곳은 switch 문입니다.

struct Icecream {
    let name: String
    let price: Int
    let isSour: Bool
}

let myIcecream = Icecream(name: "very strawberry", price: 3300, isSour: true)

switch myIcecream.isSour {
case true:
    print("\(myIcecream.name) is sour taste")
case false:
    print("\(myIcecream.name) is Non sour taste")
}

위 예시코드가 실행되면 콘솔창에 very strawberry is sour taste 가 출력됩니다.
어떻게 위와 같이 일치하는 케이스 별로 처리를 해주는 걸까요?

swift는 아래와 같은 ~= operator를 사용해서 처리합니다. 다양한 타입으로 오버로드가 가능하며, 이 연산자를 잘 사용하면 원하는 다양한 패턴매칭 처리를 해줄 수 있습니다.

func ~= (pattern: Bool, value: Bool) -> Bool {
    return pattern == value
}

예시로 가격을 가격대로 구분하여 출력해야 한다고 가정합시다.

extension Icecream {
    static func ~= (range: ClosedRange<Int>, icecream: Icecream) -> Bool {
        return range.contains(icecream.price)
    }
}

~= 연산자를 위와 같이 오버로드 해줍니다.

switch myIcecream {
case 1000...2000:
    print("1000원대 입니다.")
case 2000...3000:
    print("2000원대 입니다.")
case 3000...4000:
    print("3000원대 입니다.")
default:
    print("가격대를 알 수 없습니다.")
}

그리고 위 switch문을 실행하면 3000원 입니다 가 출력됩니다.

Limit

아쉽게도 튜플형태을 사용해서 전체를 비교하는 것은 불가능니다.

    static func ~= (pattern: (name: String, price: Int, isSour: Bool), icecream: Icecream) -> Bool {
        return pattern.name == icecream.name && pattern.price == icecream.price && pattern.isSour == icecream.isSour
    }
    
switch myIcecream {
case ("very strawberry", 3300, true):
    print("This is my icecream!")
default:
    print("Not mine")
}

이런 도전(?)을 하면 Tuple pattern cannot match values of the non-tuple type 'Icecream' 라는 컴파일 에러가 발생합니다.

Use case

struct Regex {
    let pattern: String

    static let email = Regex(pattern: "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}")
    static let phone = Regex(pattern: "([+]?1+[-]?)?+([(]?+([0-9]{3})?+[)]?)?+[-]?+[0-9]{3}+[-]?+[0-9]{4}")
}

extension Regex {
    static func ~=(regex: Regex, text: String) -> Bool {
        return text.range(of: regex.pattern, options: .regularExpression) != nil
    }
}

let email = "[email protected]"

switch email {
case Regex.email: print("email")
case Regex.phone: print("phone")
default: print("default")
}

이렇게 정규표현식을 통한 검사 및 분류를 할 때 유용하게 쓰일 수 있다고 합니다.

[inspired article

Link 🔗

Metadata

Metadata

Assignees

Labels

Swiftabout Swift

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions