Thứ Hai, 20 tháng 4, 2020

[Golang] Variadic Functions


Khái niệm về Variadic Function

Một function thường nhận một số lượng fixed arguments. Một Variadic Function là một function nhận một số lượng arguments có thể thay đổi được (a variable number of arguments). Nếu parameter cuối cùng được bắt đầu với dấu ellipsis ..., thì function có thể nhận bất kì argument nào cho parameter đó.

Chú ý rằng, chỉ có parameter cuối cùng có thể variadic.

Syntax của một variadic function:



func hello(a int, b ...int) {
}


ở trên, hello là một Variadic Function. Parameter b là một variadic parameter được prefixed bằng dấu ellipsis ... . b có thể nhận bất cứ arguments nào được truyền vào.

Ta có thể gọi func hello với số lượng argument thay đổi:
hello(1)
hello(1,2)
hello(1, 2 , 4, 5, 6 )


Trong line 1, chúng ta pass b với một zero argument. ok, it's still work fine :)
Trong line 2, chúng ta pass b với một argument 2.
Trong line 3, chúng ta pass b với 4 argument: 2, 4, 5, 6

Vậy chúng ta có thể đặt variadic parameter thành là first argument như thế này?



func hello(b ...int, a int, ) {
}


Câu trả lời là KHÔNG. Vì nếu như vậy, tất cả các argument được nhập vào sẽ được pass qua b, và argument a sẽ không nhận được giá trị nào hết :) . Và theo tính chất cân bằng, a sẽ buồn vì b nhận được quá nhiều mà mình không nhận được gì. a khiếu kiện lên Compiler và Compiler sẽ báo lỗi
 syntax error: cannot use ... with non-final parameter b

How Variadic Function Works?


Cùng phân tích ví dụ sau:
package main
import (
   "fmt")

func find(num int, nums ...int) {
   fmt.Printf("type of nums is %T\n", nums)
   found := false   for i, v := range nums {
      if v == num {
         fmt.Println(num, "found at index", i, "in", nums)
         found = true      }
   }
   if !found {
      fmt.Println(num, "not found in ", nums)
   }
   fmt.Printf("\n")
}
func main() {
   find(89, 89, 90, 95)
   find(45, 56, 67, 45, 90, 109)
   find(78, 38, 56, 98)
   find(87)
}
Trong ví dụ trên, find là một variadic function với nums là một variadic parameter. vì vậy, tất cả các arguments được truyền vào find , trừ argument đầu tiên, sẽ được pass qua nums.

Type của nums là một slice của int [] int . 

Ví dụ:
   find(45, 56, 67, 45, 90, 109)

khi đó, 56, 67, 45, 90, 109 sẽ được compiler convert thành một slice []int{56, 67, 45, 90, 109} và pass vào function find

Chạy chương trình trên ta sẽ có kết quả:
$ go run variadic_function.go 
type of nums is []int
89 found at index 0 in [89 90 95]

type of nums is []int
45 found at index 2 in [56 67 45 90 109]

type of nums is []int
78 not found in  [38 56 98]

type of nums is []int
87 not found in  []

Tại sao chúng ta không pass một slice vào một function mà lại dùng Variadic function?


1. Khi dùng Variadic function, chúng ta sẽ không cần phải khởi tạo các NEW silce trước khi call func.
2. Khi dùng Variadic function, số lượng argument được truyền vào rất linh động. Ngoài ra, có thể sử dụng variadic giống các func thông thường khi chúng ta không cần truyền vào variadic parameter. Ví dụ, ta có thể gọi find(87) như chương trình ở trên.

Append là một variadic function

Tham khảo  trong lib ta sẽ thấy:

func append(slice []Type, elems ...Type) []Type

Do đó, có thể append bao nhiêu elements vào một slice tuỳ thích (tuy nhiên, không được vượt quá capacity của slice ban đầu được tạo)

Chúng ta có thể passing một slice vào một variadic function?


Chuyên gì sẽ xảy ra nếu chúng ta gọi hàm find như sau:
func main() {
   nums := []int{89, 90, 95}
   find(89, nums)
}
code sẽ không work. Vì đơn giản, find func nhận các argument là các int. Compiler sẽ báo
cannot use nums (type []int) as type int in argument to find

Vậy chúng ta có thể passing một silce của int vào một variadic function?


Câu trả lời là YES !

Có một khái niệm gọi là syntactic sugar. syntactic sugar  cho phép chúng ta passing một slice vào một variadic function với syntax:

func find(num int, nums int...)

Ví dụ như sau:
package main
import (
   "fmt")

func find(num int, nums ...int) {
   fmt.Printf("type of nums is %T\n", nums)
   found := false   for i, v := range nums {
      if v == num {
         fmt.Println(num, "found at index", i, "in", nums)
         found = true      }
   }
   if !found {
      fmt.Println(num, "not found in ", nums)
   }
   fmt.Printf("\n")
}
func main() {
   nums := []int{89, 90, 95}
   find(89, nums...)
}


Chạy chương trình trên:
$go run variadic_function.go 

type of nums is []int
89 found at index 0 in [89 90 95]



Chúng ta có thể modify các item trong một variadic parameter không?


Câu trả lời là YES. Cùng theo dõi ví dụ sau:
package main
import (
   "fmt")

func change(s ...string) {
   s[0] = "Go"}

func main() {
   welcome := []string{"hello", "world"}
   change(welcome...)
   fmt.Println(welcome)
}

Và output:
$ go run variadic_function.go 
[Go world]


SUMMARY:


- Một Variadic func được passing một variadic parameter với dấu ellipsis ... , là last argument
- Variadic Function là rất hữu dụng. Đặc biệt là trong trường hợp số lượng arguments được truyền vào một func là không cố định.
- Chúng ta có thể pass một slice vào một variadic function sử dụng syntatic sugar.

Không có nhận xét nào:

Đăng nhận xét