[Series] Elixir programming language – part 3

4 min read

elixir
elixir

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ố ab, 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ố 23.

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_functionprivate_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

Avatar photo

Clean Code: Nguyên tắc viết hàm trong lập trình…

Trong quá trình phát triển phần mềm, việc viết mã nguồn dễ đọc, dễ hiểu là yếu tố then chốt để đảm bảo code...
Avatar photo Dat Tran Thanh
3 min read

Clean Code: Nguyên tắc comment trong lập trình

Trong lập trình, code không chỉ là một tập hợp các câu lệnh để máy tính thực thi, mà còn là một hình thức...
Avatar photo Dat Tran Thanh
3 min read

Clean Code: Nguyên tắc xử lý lỗi (Error Handling)

Trong quá trình phát triển phần mềm, việc xử lý lỗi không chỉ là một phần quan trọng mà còn ảnh hưởng trực tiếp...
Avatar photo Dat Tran Thanh
4 min read

One Reply to “[Series] Elixir programming language – part 3”

Leave a Reply

Your email address will not be published. Required fields are marked *