Scala – Closure

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ố ab 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:

  1. Chúng ta có thể truyền một đoạn code như một giá trị
  2. 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
  3. 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

 

You May Also Like

About the Author: Nguyen Dinh Thuc

Leave a Reply

Your email address will not be published.