Series học Python từ A tới Á - 3
7 min read

Series học Python từ A tới Á - 3

Phần tiếp theo

Trong phần trước, chúng ta đã được giới thiệu về lập trình hướng đối tượng (OOP) trong Python. Ở phần này chúng ta sẽ tiếp tục với lập trình hàm trong Python.

Lập trình hàm là một phong cách lập trình trong đó chương trình được viết như một tập hợp các hàm. Mỗi hàm thực hiện một nhiệm vụ cụ thể và có thể được sử dụng lại trong các chương trình khác nhau.

Lập trình hàm có một số lợi thế so với các phong cách lập trình khác, chẳng hạn như:

  • Dễ hiểu và bảo trì: Lập trình hàm có thể giúp cải thiện độ rõ ràng và khả năng bảo trì của mã nguồn bằng cách chia nhỏ chương trình thành các khối nhỏ hơn, dễ hiểu hơn.
  • Dễ dàng tái sử dụng: Các hàm có thể được sử dụng lại trong các chương trình khác nhau, giúp giảm thiểu sự trùng lặp mã và cải thiện hiệu quả.
  • Tăng khả năng mở rộng: Lập trình hàm có thể giúp dễ dàng mở rộng chương trình bằng cách thêm các hàm mới.

Functional Programming

Các khái niệm

Pure Functions:

"Pure functions" là một khái niệm quan trọng trong lập trình hàm (functional programming).

  • Xác định rõ ràng: Kết quả của một pure function chỉ phụ thuộc vào các tham số đầu vào. Nếu bạn gọi một hàm với cùng một đầu vào, nó sẽ luôn trả về cùng một kết quả. Ví dụ, nếu có một hàm add(x, y) trả về x + y, thì nó là một pure function bởi vì kết quả chỉ phụ thuộc vào xy.
  • Không có side effects: Pure functions không làm thay đổi trạng thái của chương trình hoặc thay đổi dữ liệu ngoài phạm vi của nó. Ví dụ, nếu một hàm thay đổi một biến toàn cục, ghi dữ liệu vào tệp, hoặc in dữ liệu ra màn hình, thì nó không phải là một pure function.

Các ưu điểm của pure functions:

  • Dễ dàng để kiểm tra và debug: Bởi vì pure functions luôn trả về cùng một kết quả với cùng một đầu vào, chúng dễ dàng để kiểm tra và debug.
  • Dễ dàng để tối ưu: Các hàm tinh khiết có thể được tối ưu hóa bằng cách sử dụng các kỹ thuật như memoization, đó là lưu kết quả của một hàm sau khi nó được tính toán lần đầu, và sau đó trả về kết quả đã lưu này nếu hàm được gọi lại với cùng một đầu vào.
  • Dễ dàng để song song hóa: Bởi vì pure functions không có side effects, chúng có thể được thực thi song song mà không cần phải lo lắng về việc chúng sẽ làm thay đổi trạng thái chung của chương trình.

Ví dụ về một pure function trong Python:

def add(x, y)
  return x + y

Ở đây, hàm add là một pure function bởi vì kết quả của nó chỉ phụ thuộc vào các đầu vào xy, và nó không có bất kỳ side effects nào.

Recursion

Recursion là một khái niệm quan trọng không chỉ trong lập trình hàm (functional programming) mà cũng trong lập trình nói chung.

Recursion xảy ra khi một hàm gọi chính nó, trực tiếp hoặc gián tiếp. Một hàm đệ quy thường được sử dụng để giải quyết một vấn đề mà có thể được chia thành các bài toán con tương tự nhưng nhỏ hơn.

Các yếu tố quan trọng của một hàm đệ quy:

  • Base Case: Điều kiện dừng của hàm đệ quy. Khi base case được đạt, hàm đệ quy sẽ dừng việc gọi chính nó và bắt đầu quá trình trả về kết quả.
  • Recursive Case: Trường hợp mà hàm gọi chính nó với một đầu vào nhỏ hơn hoặc đơn giản hơn.

Lập trình hàm thường ưu tiên sử dụng đệ quy vì nó đôi khi dễ hiểu hơn và sạch sẽ hơn so với việc sử dụng các vòng lặp. Tuy nhiên, đệ quy có thể dẫn đến việc sử dụng bộ nhớ nhiều hơn và có thể gây ra lỗi tràn ngăn xếp (stack overflow) nếu độ sâu của đệ quy quá lớn.

Ví dụ về một hàm đệ quy trong Python:

def factorial(n):
  // Base case
  if n == 0:
    return 1
    
  // Recursive case
  return n * factorial(n - 1)

Ở đây, hàm factorial gọi chính nó đến khi n giảm về 0, lúc đó nó trả về 1 và bắt đầu quá trình trả về kết quả.

Functions are First-Class and can be Higher-Order

Trong lập trình hàm, các hàm được coi là thứ dân sự hàng đầu, và chúng cũng có thể là hàm bậc cao. Hãy phân giải hai khái niệm này:

Functions are First-Class:

Điều này có nghĩa là các hàm có thể được xem như bất kỳ giá trị hoặc đối tượng nào khác trong ngôn ngữ. Các hàm có thể:

  • Được gán cho biến.
  • Được truyền như đối số cho các hàm khác.
  • Được trả về như giá trị từ các hàm khác.
  • Được lưu trữ trong cấu trúc dữ liệu như mảng, danh sách, đối tượng, v.v.

Ví dụ, trong Python, một hàm có thể được gán cho một biến như thế này:

def square(x):
    return x * x

my_function = square

Functions are Higher-Order:

Một hàm được coi là hàm bậc cao nếu nó thực hiện ít nhất một trong các điều sau:

  • Nhận một hoặc nhiều hàm như đối số.
  • Trả về một hàm như kết quả của nó.

Đây là một ví dụ về hàm bậc cao trong Python:

def create_adder(x):
    def adder(y):
        return x + y
    return adder

add5 = create_adder(5)
print(add5(2)) # 7

Trong ví dụ này, create_adder là một hàm bậc cao vì nó trả về một hàm khác. Hàm được trả về, add5, sau đó có thể được sử dụng như bất kỳ hàm nào khác.

Khái niệm về hàm là thứ dân sự hàng đầu và hàm bậc cao là cơ bản đối với lập trình hàm và cho phép xây dựng các hệ thống phức tạp bằng cách sử dụng các hàm đơn giản và các kỹ thuật tổng hợp.

Variables are Immutable

Trong lập trình hàm, các biến thường được coi là các biến immutable. Điều này có nghĩa là trạng thái của một biến không thể được thay đổi sau khi nó được tạo ra.

Việc sử dụng các biến immutable có một số lợi thế so với việc sử dụng các biến có thể thay đổi, chẳng hạn như:

  • Dễ hiểu và bảo trì: Mã sử dụng các biến immutable thường dễ hiểu và bảo trì hơn vì trạng thái của biến không thể thay đổi bất ngờ.
  • Dễ dàng tái sử dụng: Mã sử dụng các biến immutable có thể dễ dàng tái sử dụng trong các chương trình khác nhau vì trạng thái của biến không phụ thuộc vào trạng thái của dữ liệu bên ngoài chương trình.
  • Tăng khả năng kiểm tra: Mã sử dụng các biến immutable có thể dễ dàng kiểm tra hơn vì trạng thái của biến có thể được dự đoán dễ dàng.

Một số ví dụ về biến immutable trong Python bao gồm:

  • Số nguyên: Các số nguyên là các biến immutable trong Python. Ví dụ, biến number = 10 sẽ luôn có giá trị là 10.
  • Chuỗi: Các chuỗi là các biến immutable trong Python. Ví dụ, biến string = "Hello, world!" sẽ luôn có giá trị là "Hello, world!".
  • Tuple: Các tuple là các biến bất biến trong Python. Ví dụ, biến tuple = ("a", "b", "c") sẽ luôn có giá trị là ("a", "b", "c").

Việc sử dụng các biến immutable là một trong những nguyên tắc quan trọng của lập trình hàm. Nó có thể giúp cải thiện chất lượng của mã nguồn và làm cho mã dễ hiểu, bảo trì và kiểm tra hơn.

Dưới đây là một số mẹo để sử dụng các biến immutable trong lập trình hàm:

  • Sử dụng các biến immutable khi có thể.
  • Tránh sử dụng các biến có thể thay đổi làm tham số cho các hàm.
  • Tránh sử dụng các biến có thể thay đổi làm giá trị trả về từ các hàm.

Nếu bạn đang tìm cách cải thiện chất lượng của mã nguồn của mình, hãy thử sử dụng các biến immutable.

Functional Programming trong Python

Trong Python, bạn có thể định nghĩa một hàm bằng cách sử dụng từ khóa def, sau đó tên của hàm, và sau đó một cặp dấu ngoặc đơn chứa các tham số (nếu có). Cú pháp chung như sau:

def tên_hàm(tham_số1, tham_số2, ...):
    # mã của hàm
    return giá_trị_trả_về

Ví dụ, đây là một hàm đơn giản trong Python:

def cong(a, b):
    return a + b

# Gọi hàm
kết_quả = cong(3, 4)
print(kết_quả)
# Output: 7

Trong ví dụ trên, cong là một hàm có hai tham số: ab. Hàm này trả về tổng của ab. Để sử dụng hàm, chúng ta gọi nó và truyền các giá trị cho các tham số của nó.

Như vậy, lập trình hàm trong Python bao gồm việc tạo các hàm bằng cách sử dụng từ khóa def, sau đó gọi các hàm này khi cần thiết trong chương trình của bạn.

Enjoying these posts? Subscribe for more