Trong phần này của loạt bài viết, chúng ta sẽ khám phá một số khái niệm quan trọng trong ngôn ngữ lập trình Elixir: hàm và pattern matching. Chúng ta sẽ tìm hiểu cách định nghĩa và sử dụng hàm trong Elixir, cùng với cách pattern matching được sử dụng để kiểm tra và phân tích các giá trị.
Hàm trong Elixir
Hàm Nặc Danh (Anonymous Functions)
Hàm nặc danh là hàm không có tên, được định nghĩa một cách trực tiếp và thường được sử dụng như một đối số cho các hàm khác. Ví dụ:
# Hàm nặc danh tính tổng hai số
sum = fn a, b -> a + b end
# Sử dụng hàm nặc danh
result = sum.(2, 3)
Trong ví dụ trên, chúng ta định nghĩa một hàm nặc danh sum
nhận hai tham số a
và b
, và trả về tổng của chúng. Sau đó, chúng ta sử dụng hàm nặc danh này để tính tổng của hai số 2
và 3
.
Hàm Được Đặt Tên
Ngoài việc định nghĩa hàm trong module, Elixir cũng cho phép định nghĩa hàm được đặt tên mà không cần phải nằm trong module. Ví dụ:
# Định nghĩa hàm được đặt tên
def greeting(name) do
"Hello, #{name}!"
end
# Sử dụng hàm được đặt tên
message = greeting("Alice")
Trong ví dụ trên, chúng ta định nghĩa một hàm greeting
nhận một tham số name
và trả về một chuỗi chào Hello, {name}!
. Sau đó, chúng ta sử dụng hàm này để chào đón Alice
.
Hàm Private (Private function)
Trong Elixir, chúng ta có thể định nghĩa các hàm private trong một module để giữ cho các hàm đó chỉ được sử dụng bên trong module đó và không thể truy cập từ bên ngoài. Điều này giúp cho việc tạo ra các hàm có tính riêng tư và giảm thiểu sự phụ thuộc giữa các module.
defmodule Example do
# Hàm public
def public_function do
IO.puts("This is a public function")
private_function() # Gọi hàm private từ bên trong module
end
# Hàm private
defp private_function do
IO.puts("This is a private function")
end
end
# Sử dụng hàm public
Example.public_function()
# Example.private_function() # Lệnh này sẽ gây lỗi vì không thể truy cập hàm private từ bên ngoài module
Trong ví dụ trên, hàm public_function
được định nghĩa với từ khóa def
(có thể truy cập từ bên ngoài module), trong khi hàm private_function
được định nghĩa với từ khóa defp
(chỉ có thể truy cập từ bên trong module).
Khi gọi Example.public_function()
, chúng ta có thể thấy rằng cả hai hàm public_function
và private_function
đều được thực thi, nhưng nếu ta cố gắng gọi Example.private_function()
từ bên ngoài module, Elixir sẽ ghi nhận lỗi vì không thể truy cập hàm private từ bên ngoài module.
Sử dụng các hàm private giúp cho mã của bạn trở nên rõ ràng và dễ bảo trì, vì chúng giảm thiểu sự phụ thuộc và chỉ rõ các phần của mã chỉ được sử dụng nội bộ.
Tham số mặc định
Trong Elixir, bạn có thể định nghĩa các tham số mặc định cho các hàm. Điều này cho phép bạn gọi hàm mà không cần truyền đầy đủ các tham số và sẽ sử dụng giá trị mặc định cho các tham số không được truyền vào.
Dưới đây là cách định nghĩa và sử dụng các tham số mặc định trong Elixir:
defmodule Example do
# Định nghĩa hàm với tham số mặc định
def greet(name \\ "World") do
"Hello, #{name}!"
end
end
# Gọi hàm greet mà không truyền tham số
IO.puts(Example.greet()) # Kết quả: "Hello, World!"
# Gọi hàm greet với tham số được truyền
IO.puts(Example.greet("Alice")) # Kết quả: "Hello, Alice!"
Trong ví dụ trên, hàm greet
được định nghĩa với một tham số mặc định name \\ "World"
, có nghĩa là nếu không có tham số được truyền vào khi gọi hàm, giá trị của name
sẽ mặc định là "World"
.
Khi chúng ta gọi Example.greet()
mà không truyền tham số, hàm greet
sẽ sử dụng giá trị mặc định cho name
và trả về "Hello, World!"
. Khi chúng ta truyền tham số "Alice"
vào hàm, hàm sẽ sử dụng giá trị này cho name
và trả về "Hello, Alice!"
.
Việc sử dụng các tham số mặc định giúp làm cho mã của bạn trở nên linh hoạt và dễ đọc, đồng thời giảm bớt số lượng các trường hợp đặc biệt cần xử lý.
Pattern Matching
Pattern matching không chỉ giới hạn cho biến trong Elixir, nó còn có thể dùng cho hàm như ví dụ trong phần này.
Elixir dùng pattern matching để xác định tập tham số trùng hợp đầu tiên và gọi nội dung hàm tương ứng:
{status, message} = {:ok, "Success"}
Trong ví dụ trên, chúng ta kiểm tra nếu giá trị bên trái của phép gán phù hợp với cấu trúc {status, message}
. Nếu phù hợp, giá trị :ok
sẽ được gán cho status
và chuỗi "Success"
sẽ được gán cho message
.
Một số ví dụ khác cho bài viết:
# Định nghĩa hàm tính giai thừa
defmodule Math do
def factorial(0), do: 1
def factorial(n), do: n * factorial(n - 1)
end
# Sử dụng pattern matching để kiểm tra và tính tổng
defmodule Example do
def sum({:ok, a}, {:ok, b}) do
IO.puts("Both values are ok")
a + b
end
def sum({:error, a}, _) do
IO.puts("Error occurred with value: #{a}")
a
end
end
# Sử dụng hàm tính giai thừa
IO.puts(Math.factorial(5)) # Kết quả: 120
Kết Luận
Trong phần này, chúng ta đã tìm hiểu về hai khái niệm quan trọng trong Elixir: hàm và pattern matching. Hàm là cơ sở của mọi logic trong Elixir, trong khi pattern matching giúp kiểm tra và phân tích các giá trị một cách linh hoạt và mạnh mẽ. Trong phần tiếp theo của loạt bài viết này, chúng ta sẽ khám phá về concurrency và parallelism trong Elixir. Đừng quên đón chờ nhé!
Tham khảo bài viết trước tại link
One Reply to “[Series] Elixir programming language – part 3”