Tìm hiểu Higher-Order Functions và Lambda trong Kotlin

2
Dịch vụ dạy kèm gia sư lập trình
Bài này thuộc phần 3 của 5 phần trong series Kotlin Android cơ bản

Phiên bản Java 8 được release vào năm 2014 cùng với một tính năng rất quan trọng đó là Lambda expressions. 2 năm sau thì Kotlin ra đời, tất nhiên tính năng hay ho đã được Kotlin học hỏi. Vậy Lambda trong Kotlin là gì? Nó khác với lambda trong java không?

Thực ra, khái niệm lambda này có lẽ là được học hỏi từ các ngôn ngữ như Javascript hay Python. Nó gần giống với Callback trong Javascript vậy.

Tuy nhiên, với những người làm việc lâu năm với Java thì sẽ hơi khó “nuốt trôi” khái niệm mới này. Nhưng nó lại là một khái niệm rất quan trọng và hay sử dụng trong Kotlin.

Vì vậy, để các bạn có thể hiểu rõ về Lambda function, bài viết này mình sẽ chia sẻ những hiểu biết của mình về Lambda trong kotlin.

Chúng ta cùng bắt đầu nhé!

Higher-Order Functions và Lambdas trong Kotlin

#Higher-Order Function là gì?

Theo định nghĩa từ chính nhà phát hành Kotlin: Higher-Order Function là kiểu function có thể sử dụng các function như một tham số hay trả về một phương thức.

Khái niệm này rất giống với khái niệm sử dụng hàm như tham số trong Javascript vậy.

Tóm lại, Higher-Order Function có hai đặc điểm chính là:

  • Có thể lấy các function làm tham số.
  • Có thể trả về một function.

Ví dụ, mình định nghĩa một function như sau:

fun showWelcome(strWelcome: String, myfunc: (String) -> Unit) {
    print("Welcome to Kotlin tutorial at ")
    myfunc(strWelcome)
}

Theo như định nghĩa hàm ở trên,  tham số của hàm passMeFunction là một hàm khác: (String) -> Unit.

Trong đó:

  • (String) nghĩa là function này có 1 tham số truyền vào là một String.
  • Unit nghĩa là function này không trả về gì cả( Giống với kiểu void trong Java vậy).

Còn đây là cách sử dụng:

fun main(args: Array<String>) {
   showWelcome("VNTALKING", {str: String -> println(str)})
}

fun showWelcome(strWelcome: String, myfunc: (String) -> Unit) {
    print("Welcome to Kotlin tutorial at ")
    myfunc(strWelcome)
}

Kết quả chạy chương trình sẽ như sau:

higher order functions trong kotlin

Thông qua ví dụ này, bạn đã hiểu đặc điểm cho phép truyền một function như một tham số trong một function rồi đúng không?

Giờ chúng ta sẽ làm rõ đặc điểm thứ 2: Một function có thể trả về kết quả là một function.

Giả sử chúng ta có hàm thực hiện phép tính cộng add như sau:

fun add(a: Int, b: Int): Int = a + b

Sau đó chúng ta định nghĩa một hàm returnMeAddFunction không có tham số truyền vào và tại vùng trả về( sau dấu 🙂 là một hàm: ((Int, Int) -> Int): Hàm này có hai tham số truyền vào kiểu Int, trả về kết quả cũng là kiểu Int

fun returnMeAddFunction(): ((Int, Int) -> Int) {
     // can do something and return function as well
     // returning function
     return ::add
}

Nhìn vào đoạn code trên, chúng ta thấy rằng: hàm returnMeAddFunction thực hiện return lại hàm add.

Còn đây là cách sử dụng:

fun main(args: Array<String>) {
   val add = returnMeAddFunction()

val result = add(10, 20)
    println("10 + 20 = ${result}")
}
fun returnMeAddFunction(): ((Int, Int) -> Int) {
     // can do something and return function as well
     // returning function
     return ::add
}
fun add(a: Int, b: Int): Int = a + b

Kết quả khi chạy chương trình như bên dưới

lambdas trong kotlin

Note: Các bạn có thể nghịch thử Kotlin mà không cần Android Studio tại đây

>> Rất có ích cho bạn: Khóa học miễn phí Kotlin(Video): Từ cơ bản tới nâng cao

# Lambda trong Kotlin là gì?

Về cơ bản thì Lambda expression hay gọi tắt là lambdas là một hàm ẩn danh (anonymous function) được định nghĩa bằng hai ký tự {} mà bạn có thể coi là một giá trị.

Ta có thể truyền chúng làm tham số cho các function, trả về một lambda expression hoặc bất kỳ điều gì mà chúng ta có thể làm với một đối tượng bình thường.

Ví dụ: để tạo một lambda expression chỉ in ra dòng “Hello Lambdas!” thì viết như sau:

val printHelloWorld = {
   println("Hello Lambdas!")
}

Và khi gọi hàm thì chúng ta gọi như một hàm bình thường.

printHelloWorld()
// or
printHelloWorld.invoke()

Nếu bạn muốn truyền tham số vào hàm lambda thì làm như sau:

val sayHello = { user: String -> 
   println("Hello, $user!") 
}
sayHello("johnny")
// or with multiple parameters
val printSummary = { user: String, score: Int -> 
   println("User '$user' get $score points.")
}
printSummary("johnny", 123)

Nếu kiểu của tham số có thể suy ra từ ngữ cảnh(context) thì có thể bỏ qua chúng như bên dưới:

val names = arrayOf("joe", "ann", "molly", "dolly")
names.sortedBy { name -> name.length }
// Tương đương với cách viết sau
names.sortedBy { name: String -> name.length }

Khi bạn làm việc với thư viện Kotlin Sequence, bạn hay phải định nghĩa các hàm short function literals( một cách gọi khác của lambda expression) mà chỉ có một tham số:

val russianNames = arrayOf("Maksim", "Artem", "Sophia", "Maria", "Maksim")

val selectedName = russianNames
      .filter { name -> name.startsWith("m", ignoreCase = true) }
      .sortedBy { name -> name.length }
      .firstOrNull()

Trong trường hợp đặc biệt này, Kotlin cho chúng ta hai cách viết

.filter { name -> name.startsWith("m", ignoreCase = true) }
// Hoặc chúng ta có thể viết như sau
.filter { it.startsWith("m", ignoreCase = true) }

#Function types

Trong phần tìm hiểu Higher-Order Function ở trên, chúng ta có gặp một cách định nghĩa hàm khá đặc biệt.  Phần này mình sẽ chỉ mang tính chất tổng hợp lại thôi.

Thực ra Kotlin cung cấp một số cú pháp ngắn gọn để bạn định nghĩa các loại hàm.

Ví dụ: ()->Unit: Định nghĩa một hàm không tham số nào và không trả về gì. Các viết này tương đương với cách viết void doSomeThings() { ... } trong java.

Hay (Int)->Int: Định nghĩa một hàm mà có 1 tham số kiểu Integer, và trả về kết quả là một integer. Tương đương trong Java, ta có int doSomeThings(int arg) { .... }

val fun1: (Int,Int)->Int = 
   { a,b -> Math.max(a,b) }

val fun2: (String,MutableList<String>)->Unit =
   { s,list -> list.add(s) }

val fun3: (Int,(Int)->Int)->Int = 
   { value, func -> func(value) }

Với cách việc của hàm fun1, fun2 thì các bạn đều hiểu cả. Tuy với cách viết của hàm fun3 thì chắc hơi khó hiểu đúng không?

Với hàm fun3, khác với bình thường là thay vì trả về một kiểu quen thuộc như: Int, String,… hay Object thì nó lại trả về một function khác.

#Tạm kết

Như vậy, qua bài viết này chúng ta đã cùng nhau tìm hiểu những khái niệm rất cơ bản về Higher-Order Functions và Lambda trong kotlin.

Với cá nhân mình thì mình không thích cách viết của Kotlin lắm. Cảm giác cứ nửa mùa. Không hẳn kiểu thoải mái như Javascript, cũng không hẳn chặt chẽ như Java.

Giờ Google đã quyết chọn Kotlin là một trong những ngôn ngữ chính cho ứng dụng Android. Đành phải cố thôi 🙂 >>> Ngôn ngữ Kotlin – Tại sao phải học?

Mình hi vọng bài viết có ích cho các bạn, ủng hộ mình bằng một bình luận nhé!

Xem tiếp các bài trong Series
Phần trước: Kotlin Android Fragment, Activity, & Dialog FragmentPhần kế tiếp: Sử dụng Volley library Android để thao tác với Network API
Dịch vụ phát triển ứng dụng mobile giá rẻ - chất lượng
Bài trướcTạo Push Notification với Firebase trong ứng dụng Android
Bài tiếp theoNguyên lý SOLID trong Node.js với TypeScript
Sơn Dương
Tên đầy đủ là Dương Anh Sơn. Tốt nghiệp ĐH Bách Khoa Hà Nội. Mình bắt đầu nghiệp coder khi mà ra trường chẳng xin được việc đúng chuyên ngành. Mình tin rằng chỉ có chia sẻ kiến thức mới là cách học tập nhanh nhất. Các bạn góp ý bài viết của mình bằng cách comment bên dưới nhé !

2
Bình luận. Cùng nhau thảo luận nhé!

avatar
  Theo dõi bình luận  
Mới nhất Cũ nhất Nhiều voted nhất
Thông báo
duy anh
Guest
duy anh

A nên nói thêm mấy function này ứng dụng vào mục đích cụ thể nào trong android sẽ dễ hình dung và dễ áp dụng hơn.