Trong bài viết này chúng ta sẽ cùng nhau tìm hiều về Closure trong Scala.
Vấn đề
Bạn sẽ làm gì khi muốn truyền một hàm xung quanh như một biến? Làm thế nào để hàm tham chiếu đến một hoặc nhiều trường trong cùng một phạm vi? Clousure giúp ta giải quyết được vấn đề này.
Closure là gì?
Một function có giá trị trả về dựa trên các biến được khai báo bên ngoài thì được gọi là Closure, tương tự như trong Python.
Chúng ta đã biết làm thế nào để khai báo một anonymous function trong Scala
scala> val sum=(a:Int,b:Float)=>a+b sum: (Int, Float) => Float = $$Lambda$1101/539731466@12ebcdf6
Hãy thử gọi nó
scala> sum(2,3) res2: Float = 5.0
Chúng ta truyền vào 2 tham số 2 và 3, hàm trả về kết quả là 5
Nhưng hãy thử xem chúng ta gọi trong trường hợp này thì kết quả ra sao
scala> var c=7 c: Int = 7 scala> val sum1=(a:Int,b:Int)=>(a+b)*c sum1: (Int, Int) => Int = $$Lambda$1103/1497170291@4a0a93ce
Ở đây hàm sum1
tham chiếu đến các biến/giá trị c
, không phải tham số chính thức của nó, nào hãy thử gọi lại hàm này
scala> sum1(2,3) res3: Int = 35
Trong khi hàm sum
là một hàm đóng, không bị side effect, thì hàm sum1
luôn luôn tham chiếu đến c
khi chúng ta gọi nó và đọc giá trị hiện tại của nó. Nào hãy thử thay đổi giá trị của c
scala> c=8 c: Int = 8
Chúng ta đã thay đổi giá trị của c
từ 7 thành 8. Nào hãy thử gọi lại hàm sum1
scala> sum1(2,3) res5: Int = 40
Như bạn có thể thấy, nó đang sử dụng giá trị hiện tại của c
Nếu như bạn là lập trình viên đã quen với các ngôn ngữ c++ hay java, đoạn code trên có thể khiến bạn cảm thấy hơi khó hiểu. Nó đã không sử dụng chính xác giá trị của c khi khai báo lần đầu tiên, nó tìm lại những thay đổi của nó khi chúng ta gọi lại hàm này lần thứ 2. Nào hãy cùng tìm hiểu xem nó hoạt động như thế nào.
Trong ví dụ trên, 2 tham số a
và b
là những tham số chính thức của hàm sum1
. c
là một tham chiếu đến một biến trong phạm vi kèm theo. Scala đóng trên c
ở đây.
Paul Cantrell đã đề cập đến 3 tiêu chí cho Closure như sau:
- Chúng ta có thể truyền một đoạn code như một giá trị
- Bất cứ khi nào, chúng ta có thể sử dụng giá trị này để thực thi chạy đoạn code
- Nó có thể tham chiếu đến các biến trong bối cảnh mà chúng ta tạo nó.
Nó giống như 2 người yêu nhau nhưng bị chia tách bởi khoảng cách, nhưng được liên kết với nhau bằng tình cảm (trái tim). Không chỉ giúp họ nhớ đến nhau, họ có thể cảm nhận được những gì xảy ra với đối phương.
Nào hãy cùng thử ví dụ khác
scala> var age=22 age: Int = 22 scala> val sayhello=(name:String)=>println(s"I am $name and I am $age") sayhello: String => Unit = $$Lambda$1065/467925240@78b9155e scala> sayhello("Ayushi")
Kết quả sẽ trả về I am Ayushi and I am 22
Nào chúng ta thử sử dụng hàm func nhận 2 tham số đầu vào: một function, một string và gọi hàm trên
scala> def func(f:String=>Unit,s:String){ | f(s) | } func: (f: String => Unit, s: String)Unit scala> func(sayhello,"Ayushi")
Kết quả sẽ trả về I am Ayushi and I am 22
Kiểu dữ liệu
Để hiểu rõ hơn, chúng ta sẽ thử một vài ví dụ sử dụng kiểu dữ liệu khác nhau
Integer Arrays
Chúng ta sẽ thử tạo một mảng interger. Hãy khai báo một mảng với 3 số như sau:
scala> val nums=Array(1,2,4) nums: Array[Int] = Array(1, 2, 4)
Tiếp theo chúng ta định nghĩa một hàm
scala> val saynum=(a:Int)=>println(s"The number is $a") saynum: Int => Unit = $$Lambda$1194/2122460177@5b810b54
Nào hãy thử chạy như sau
scala> for(i<-0 to 2){ | saynum(nums(i)) | }
Kết quả
The number is 1 The number is 2 The number is 4
ArrayBuffer
Tiếp theo sẽ thử với một ArrayBuffer. Nhưng đầu tiên chúng ta cần import nó trước đã
scala> import scala.collection.mutable.ArrayBuffer import scala.collection.mutable.ArrayBuffer
Chúng ta khai báo một ArrayBuffer với 3 string
scala> val colors=ArrayBuffer("Red","Green","Blue") colors: scala.collection.mutable.ArrayBuffer[String] = ArrayBuffer(Red, Green, Blue)
Cuối cùng, tạo một hàm func để gọi sayhello
scala> val func=(f:String=>Unit,s:String)=>f(s) func: (String => Unit, String) => Unit = $$Lambda$1270/151863667@5d16f27b
Nào giờ hãy thử dùng một vòng lặp để gọi từng giá trị bên trong ArrayBuffer
scala> for(i<-0 to 2){ | func(sayhello,colors(i)) | }
Kết qủa
I am Red and I am 22 I am Green and I am 22 I am Blue and I am 22