DEV Community

SameX
SameX

Posted on

HarmonyOS Next Advanced Tips for Pattern Matching in Deconstruction: Nesting, Combination and Pattern Guard

In HarmonyOS Next development, the power of pattern matching is not only the matching of basic values, but also its hierarchical deconstruction ability of complex data structures.Through nested modes, combined modes and Pattern Guard, developers can efficiently process multi-level data and achieve accurate conditional filtering.This article is based on Cangjie language document, advanced techniques for analyzing pattern matching and its application in actual scenarios.

1. Nesting mode: Hierarchical destruction of multi-layer data structures

Nested mode allows other modes to be included in the schema, suitable for parsing nested enums, tuples, or object data.

1. Destruction of enum nested enums

enum Outer {
    | Inner(InnerEnum)
}
enum InnerEnum {
    | Value(String)
    | Number(Int)
}

let data = Outer.Inner(InnerEnum.Value("hello"))

match (data) {
case Outer.Inner(InnerEnum.Value(str)) => // Double-layer enum nested matching
println("Internal string: \(str)") // Output: hello
    case Outer.Inner(InnerEnum.Number(n)) =>
println("Internal number: \(n)")
}
Enter fullscreen mode Exit fullscreen mode

2. Destruction of tuple nested enumerations

let nestedTuple = (1, Outer.Inner(InnerEnum.Number(42)))

match (nestedTuple) {
case (index, Outer.Inner(InnerEnum.Number(n))) => // Tuple + Enumeration nesting
println("Number at index \(index): \(n)") // Output: Number at index 1: 42
    default => ()
}
Enter fullscreen mode Exit fullscreen mode

3. The execution order of nested patterns

The nested pattern matches layer by layer from outside to inside, and if the outer layer fails, the entire branch will be skipped directly:

let value = ("error", 404)

match (value) {
case ("success", code) => println("Success code: \(code)") // The outer string does not match, just skip it
case ("error", code) where code >= 400 => println("Error code: \(code)") // Match successfully
}
Enter fullscreen mode Exit fullscreen mode

2. Combination mode: logical or with pattern collection

Connect multiple patterns through | to achieve "logical or" matching, suitable for scenarios where similar processing logic is merged.

1. Enumeration constructor combination matching

enum Action {
    | Click | DoubleClick | LongPress(Int)
}

func handleAction(action: Action) {
    match (action) {
case Click | DoubleClick => // Combination matching click class operation
println("handle click event")
        case LongPress(duration) =>
println("Long press\(duration) milliseconds")
    }
}
Enter fullscreen mode Exit fullscreen mode

2. Combination of numerical ranges and constants

let number = 15

match (number) {
case 0 | 1 | 2 => println("small number")
case 3..10 | 15 => // Combining match ranges with single values
println("medium number or 15") // Output: medium number or 15
case _ => println("big number")
}
Enter fullscreen mode Exit fullscreen mode

3. Limitations of combination modes

  • Disable the definition of bound variables with the same name in combination mode:
// Counterexample: Repeat variable name n
case Add(n) | Sub(n) => println("operand: \(n)") // Compile error
Enter fullscreen mode Exit fullscreen mode
  • The patterns in the combined mode must belong to the same type category (such as both are enumeration constructors or are numerical values).

3. Pattern Guard: Enhancement of Conditional Filtering

Pattern Guard adds additional conditions to the pattern through the where clause to achieve finer matching logic.

1. Basic syntax structure

match (value) {
case pattern where conditional expression => processing logic
}
Enter fullscreen mode Exit fullscreen mode

2. Conditional filtering of enumeration parameters

enum Temperature {
    | Celsius(Float) | Fahrenheit(Float)
}

let temp = Celsius(38.5)

match (temp) {
case Celsius(c) where c > 37.5 => // Match high temperature scenes
println("Abnormal body temperature:\(c)℃") // Output:Abnormal body temperature: 38.5℃
    case Celsius(c) =>
println("Normal body temperature:\(c)℃")
    case Fahrenheit(f) where f > 100 =>
println("High temperature:\(f)℉")
}
Enter fullscreen mode Exit fullscreen mode

3. Complex conditions combined with binding mode

let point = (x: 5, y: 5)

match (point) {
case (x, y) where x == y && x > 0 => // Use the binding variable x/y to participate in the conditional judgment
println("First quadrant diagonal point: (\(x), \(y))") // Output: First quadrant diagonal point: (5, 5)
    case (x, y) where x < 0 && y < 0 =>
println("third quadrant point")
    default => ()
}
Enter fullscreen mode Exit fullscreen mode

4. Combining mode guard and type mode

class Person {
    let age: Int
    init(age: Int) { self.age = age }
}

let person = Person(age: 25)

match (person) {
case p: Person where p.age >= 18 => // Type mode + age condition
println("adult")
    case p: Person =>
println("Minor")
}
Enter fullscreen mode Exit fullscreen mode

4. Practical scenario: Accurate analysis of protocol data

Scenario: parse custom communication protocols

Protocol format: [Type Identification] [Length] [Data Content], where the type Identification is0x01(text) or0x02` (value).

1. Enumeration definition matches pattern

`cj
enum ProtocolData {
| Text(String)
| Number(Int)
}

func parsePacket(bytes: [UInt8]) -> ProtocolData? {
guard bytes.count >= 3 else { return None }

match (bytes[0], bytes[1..3]) {
Enter fullscreen mode Exit fullscreen mode

case (0x01, lengthBytes) where let length = bytesToInt(lengthBytes) => // Mode guard calculates length
let dataBytes = bytes[3..3+length]
return .Text(utf8ToString(dataBytes))
case (0x02, lengthBytes) where let length = bytesToInt(lengthBytes) =>
let dataBytes = bytes[3..3+length]
return .Number(bytesToInt(dataBytes))
default => return None
}
}
`

2. The key role of pattern guard

  • Verify the validity of the length field (such as length > 0);
  • Handling possible parsing through guards fails when converting a byte array to a numeric value or string.

5. Precautions and best practices

1. Avoid overly complex patterns

  • When the pattern is nested more than three layers or the conditions are too complex, split into independent functions or types;
  • Priority is given to using named parameters and clear indentation to improve readability.

2. The importance of pattern order

  • Put specific patterns (such as enumeration constructors with conditions) before general mode;
  • The combination mode and guarding conditions should be arranged from strict to loose.

3. Optimize with compiler prompts

The Cangjie compiler will prompt unused binding variables or unreachable pattern branches, which can simplify the code:
cj
match (value) {
case (x, y) where x == y => println("equal") // If x/y is not used, the compiler prompts to delete the binding
default => ()
}

Summarize

Advanced techniques for pattern matching (nesting, combinatorial, pattern guard) enable HarmonyOS Next developers to efficiently process complex data structures and implement precise conditional logic:

  • Nested pattern is used to deconstruct multi-layer data, such as enum nesting or tuple nesting;
  • Combination modeCombination similar processing logic to reduce redundant branches;
  • Mode Guard Add extra conditions to achieve fine-grained matching control.

Top comments (0)