Pattern Matching trong Scala

Blog này viết về Pattern Matching, một nội dung khá là thú vị trong Scala

Vấn đề:
Bạn cần chọn ra một hoặc nhiều pattern phù hợp trong một biểu thức.
(Pattern đó có thể là hằng, là biến, là tuple,….)
Giải pháp:
Xác định trường hợp cho mỗi pattern mà bạn mong muốn.
Thông thường, khi sử dụng pattern matching, các phương thức của bạn sẽ thể hiện các kế thừa từ một base class hoặc trait. Và sau đó nó sẽ match vào các trường hợp cụ thể.

import java.io.File
sealed trait RandomThing
case class RandomFile(f: File) extends RandomThing
case class RandomString(s: String) extends RandomThing
class RandomNoiseMaker {
    def makeRandomNoise(t: RandomThing) = t match {
        case RandomFile(f) => playSoundFile(f)
        case RandomString(s) => speak(s)
    }
}

Hàm makeRandomNoise() khai báo biến t kiểu RandomThing và sau đó, nó sẽ match t với các kiểu có RandomFile và RandomString. Nếu là RandomFile thì sẽ gọi hàm playSoundFile(), nếu là RandomString sẽ gọi hàm speak().

Phân loại Patterns:
Chúng ta có các loại patterns như sau
Constant patterns: là một pattern chỉ có thể match với chính nó. Ví dụ

    case 0 => "zero"
    case true => "true"
    case "hello" => "you said 'hello'"
    case Nil => "an empty List"

Variable patterns: là một pattern được thể hiện bởi dấu _ , hoặc một tên biến bất kỳ. Variable patterns đại diện cho các trường hợp còn lại của phép toán match và thường nằm ở case cuối cùng.

   case _ => s"Hmm, you gave me something ..."
   case foo => s"Hmm, you gave me a $foo"

Constructor patterns: Constructor patterns cho phép bạn match một constructor với một case cụ thể.

    case Person(first, "Alexander") => s"found an Alexander, first name = $first"
    case Dog("Suka") => "found a dog named Suka"

Sequence patterns: cho phép match pattern với các sequences như List, Array, Vector,…. Ngoài việc sử dụng _ như ở Variable Patterns, bạn có thể sử dụng _ * . Toán tử này được hiểu là nhiều hơn hoặc bằng 0 các element

    case List(0, _, _) => "a three-element list with 0 as the first element"
    case List(1, _*) => "a list beginning with 1, having any number of elements"
    case Vector(1, _*) => "a vector starting with 1, having any number of elements"

Tuple patterns: cho phép match tuple patterns và truy cập giá trị các phần tử thuộc tuple. Bạn cũng có thể sử dung _ nếu không muốn lấy giá trị của phần tử đó.

    case (a, b) => s"got $a and $b"
    case (a, b, c) => s"got $a, $b, and $c"

Type patterns: Xem ví dụ bên dưới,

    case s: String => s"you gave me this string: $s"
    case i: Int => s"thanks for the int: $i"
    case f: Float => s"thanks for the float: $f"
    case a: Array[Int] => s"an array of int: ${a.mkString(",")}"
    case as: Array[String] => s"an array of strings: ${as.mkString(",")}"
    case d: Dog => s"dog: ${d.name}"
    case list: List[_] => s"thanks for the List: $list"
    case m: Map[_, _] => m.toString  

Thêm biến vào patterns:

Chúng ta theo dõi pattern sau:

def addingVar(x: Any): String = x match{
    case l: List[_] => s"$l" 
        case _ => "unknown"
}

Nếu muốn x là List có phần tử đầu tiên là 0 thì làm thế nào?
* Sửa lại thành case List(0,*)? Nhưng tôi vẫn muốn trả về một String của List đó.
* Sửa lại thành case l: List(0,
*)? Trông có vẻ đúng, biên dịch xem sao nhé.

Error:(45, 17) '=>' expected but '(' found.
    case x: List(0,_*) => s"$x"

Để giải quyết vấn đề này, chúng ta có thể dùng như sau:

def addingVar(x: Any): String = x match{
    case l: List[_] if l.head == 0 => s"$l"
    case _ => "unknown"
  }

Ngoài ra, ta cũng có thể sử dụng một variable-binding pattern
Cú pháp:

variableName @ pattern

Cụ thể với trường hợp trên:

case l @ List(0,_*) => s"$l"

Hẹn gặp lại các bạn trong những blog sau.

Tham khảo: How to use Pattern matching scala match case expressions- Alvinalexander

Add a Comment