Parttern Matching Anonymous Function
Trong Scala thì chúng ta ai cũng nói đến functional programming , và functional programming cũng là một chủ đề có lẽ được nói đến nhiều và cũng có nhiều vấn đề được bàn luận. Hôm nay mình xin đề cập đến một vấn đề trong functional programming đó là Parttern Matching Function Anonymous.
Giả sử mình có một List các tên bài hát , bạn muốn tìm kiếm chỉ mục để chuyển đổi về chữ thường thì bạn chỉ cần khai báo một hàm Anonymous thông qua hàm map như thế này:
val songTitles = List(" The White Hare ", " Childe the Hunter ", " Take no Rogues ")
songTitles.map( t => t.toLowerCase )
Hoặc Nếu bạn muốn bạn có thể viết một cách ngắn gọn hơn, tất nhiên , bạn có thể viết một hàm bình thường,sử dụng cú pháp “placeholder” trong Scala
songTitles.map(_.toLowerCase)
Vậy thì làm thế nào cú pháp này thực hiện, chúng ta cùng xem ví dụ khác dưới đây.
Tôi có một chuỗi các cặp, từng đại diện, từng đại diện cho một từ và giá trị của nó trong đoạn text. Mục tiêu của chúng ta là lọc ra những cặp có giá trị thấp hoặc cao một ngưỡng nhất định và sau đó trả về các từ còn lại mà không cần giá trị tương ứng của từ đó. Vậy chúng ta cần viết một hàm:
wordsWithoutOutliers(wordFrequencies: Seq[( String, Int )]): Seq[String]
Giải pháp ở đây là tôi đưa ra là: chúng ta sử dụng hàm filter và map đi qua các hàm Anonynous sử dụng các cú pháp bình thường :
val wordFrequencies = (" habitual ", 6) :: (" and ", 56) :: (" consuetudinary ", 2) :: (" additionally ", 27) :: (" homely ", 5) :: (" society ", 13) :: Nil
def wordsWithoutOutliers(wordFrequencies: Seq[ ( String, Int ) ]): Seq[String] =
wordFrequencies.filter(wf => wf._2 > 3 && wf._2 < 25).map(_._1)
wordsWithoutOutliers(wordFrequencies) // List("habitual", "homely", "society")
Nhưng giải pháp này có một số vấn đề. Vấn đề là nó quá dài dòng . Rất may Scala cũng cấp cho chúng ta một cách thay thế các hàm anonymous đó là : Một parttern matching anonymous fuction là một hàm anonymous được định nghĩa như các blocks gồm các trường hợp, nhưng không có từ khóa “match” trước khối lệnh. Giờ thì chúng ta viết lại hàm trên để thấy sự khác biệt nhé :
def wordsWithoutOutliers(wordFrequencies: Seq[(String, Int)]): Seq[String]=
wordFrequencies.filter { case (_, f) => f > 3 && f < 25 } map { case (word, _) => word }
Trong ví dụ này ,tôi đã chỉ được một trường hợp duy nhất trong mỗi anonymous function của chúng ta, bởi vì chúng ta biết rằng trường hợp này luôn đúng, tôi chỉ đơn giản là chỉnh sửa lại cấu trúc dữ liệu về kiểu của chúng ta đã biết tại một thời điểm biên dịch, vì vậy không có gì có thể sai ở đây. Đây là một cách rất phổ biến của việc sử dụng parttern matching anonymous function.
Nếu bạn cố gắng để gán giá trị cho các anonymous function thì bạn sẽ phải có loại dữ liệu
val predicate: ((String, Int)) => Boolean = { case (_, frequencies) => frequencies > 3 && frequencies < 25 }
val transformFn: ((String, Int)) => String = { case (word, _) => word }
Hãy chú ý, bạn phải xác định kiểu của các giá trị, vì nếu không xác định kiểu của các giá trị thì Scala sẽ không xác định được kiểu dữ liệu nào phù hợp với parttern matching anonymous function.
Không có gì có thể ngăn cản được bạn từ việc xác định một trường hợp phức tạp hơn, nếu bạn khai báo một anonymous function và muốn thông qua một hàm khác. Chẳng hạn như ví dụ trên đề cập, thì bạn phải chắc chắn tất cả các giá trị đầu vào, phải là một trường hợp của anonymous function và luôn luôn trả về giá trị, nếu không trả về giá trị thì bạn sẽ nhận được MathError tại thời điểm runtime.
Rất mong được sự góp ý từ mọi người !
Nguồn tham khảo: http://danielwestheide.com/blog/2012/12/12/the-neophytes-guide-to-scala-part-4-pattern-matching-anonymous-functions.html