Functional Programming (FP) là lựa chọn tối ưu cho việc xử lý song song, đặc biệt khi cần xử lý luồng dữ liệu từ nhiều nguồn khác nhau. FP giúp đảm bảo tính toàn vẹn của dữ liệu và không gây ra xung đột khi thực hiện các phép tính song song và phân tích dữ liệu thời gian thực.
1. Functional Programming là gì?
Lập trình hàm (functional programming) là một mô hình lập trình (programming paradigm) dựa trên việc sử dụng hàm như là nền tảng (building block) cơ bản của chương trình. Trong lập trình hàm, dữ liệu được xem là bất biến (immutable) và các hàm phải thuần túy (pure functions), mình sẽ giải thích chi tiết các khái niệm này trong phần tiếp theo nhé.
Mô hình lập trình (programming paradigm) là phong cách hay cách tư duy khi viết các chương trình phần mềm. Các mô hình lập trình phổ biến hiện nay bao gồm:
2. Những đặc điểm của Functional Programming
2.1 Các hàm phải thuần tuý
Hàm thuần túy (Pure Function) là một hàm có hai đặc tính chính:
- Không có tác dụng phụ (No Side Effects): Hàm không thay đổi bất kỳ trạng thái bên ngoài nào, không tương tác với dữ liệu bên ngoài như biến toàn cục, không đọc/ghi tệp, không in ra màn hình, không thay đổi đối số truyền vào. Nó chỉ sử dụng những dữ liệu mà nó nhận vào để thực hiện tính toán và trả về kết quả.
- Nhất quán (Deterministic): Với cùng một đầu vào, hàm thuần túy luôn trả về kết quả giống nhau, không phụ thuộc vào trạng thái bên ngoài hay bất kỳ yếu tố nào khác. Điều này giúp dễ dàng dự đoán kết quả và kiểm thử hàm.
# --- Hàm thuần tuý ---
def add(a, b):
return a + b
# Luôn trả về kết quả 5 cho cùng đầu vào (2, 3)
print(add(2, 3)) # Kết quả: 5
# --- Hàm không thuần tuý ---
total_sum = 0
def add_to_total(value):
global total_sum
total_sum += value
# Kết quả phụ thuộc vào trạng thái biến toàn cục
add_to_total(5)
print(total_sum) # Kết quả: 5
add_to_total(10)
print(total_sum) # Kết quả: 15
2.2 Dữ liệu có tính bất biến
Dữ liệu có tính bất biến (Data Immutability) là khi một giá trị hoặc một đối tượng được tạo ra, nó không thể thay đổi, lập trình hàm sẽ tạo ra bản sao mới và bổ sung những thay đổi cần thiết trên bản sao đó thay vì chỉnh sửa bản gốc.
original_list = [1, 2, 3]
# Thay vì thêm phần tử 4 vào original_list, Python tạo ra một danh sách mới, sau đó thêm phần tử 4 và gán vào new_list
new_list = original_list + [4]
print("Original List:", original_list) # Kết quả: [1, 2, 3]
print("New List:", new_list) # Kết quả: [1, 2, 3, 4]
2.3 Phù hợp với Đệ quy
Như đã đọc ở trên Functional Programming có một nguyên tắc quan trọng là tính bất biến của dữ liệu (immutability), có nghĩa là bạn không thể thay đổi biến trực tiếp khi lặp qua các phần tử, như trong một vòng lặp thông thường. Thay vì đó, đệ quy được sử dụng để xử lý các tác vụ lặp lại mà không cần thay đổi trạng thái hoặc biến.
# Vi phạm nguyên tắc bất biến
def sum_list(lst):
total = 0 # Biến trạng thái có thể thay đổi
for num in lst:
total += num # Thay đổi giá trị của total mỗi lần lặp
return total
print(sum_list([1, 2, 3, 4, 5])) # Kết quả: 15
# Không vi phạm nguyên tắc bất biến
def sum_list_recursive(lst):
if not lst: # Trường hợp cơ sở: danh sách rỗng trả về 0
return 0
else:
return lst[0] + sum_list_recursive(lst[1:]) # Đệ quy mà không thay đổi biến
print(sum_list_recursive([1, 2, 3, 4, 5])) # Kết quả: 15
2.4 Hỗ trợ Cài đặt hàm
Cài đặt hàm (function configuration) trong lập trình hàm là một kỹ thuật cho phép bạn tạo ra một hàm mới từ một hàm có sẵn bằng cách "cấu hình" một phần các tham số đầu vào của nó, giúp bạn tạo ra các hàm chuyên biệt hơn từ những hàm tổng quát, đồng thời tái sử dụng code một cách hiệu quả.
Những kỹ thuật phổ biến nhất của cấu hình hàm là:
- Partial Application: tạo ra một hàm mới bằng cách "đóng băng" một số đối số của hàm gốc, hàm mới sẽ chờ nhận các đối số còn lại.
from functools import partial
# Hàm ban đầu
def multiply(a, b):
return a * b
# Cấu hình hàm (Partial Application)
double = partial(multiply, 2) # Đặt a = 2
# Sử dụng hàm đã cấu hình
print(double(5)) # Kết quả: 10 (tương đương với multiply(2, 5))
- Currying: currying biến đổi một hàm có nhiều tham số thành một chuỗi các hàm đơn tham số. Thay vì truyền tất cả các tham số cùng lúc, bạn có thể truyền từng tham số một, với mỗi lần truyền sẽ trả về một hàm mới chờ tham số tiếp theo.
# Hàm currying với 2 tham số
def multiply(a):
def inner_multiply(b):
return a * b
return inner_multiply
# Cấu hình hàm
double = multiply(2)
# Sử dụng hàm đã cấu hình
print(double(5)) # Kết quả: 10 (tương đương với multiply(2)(5))
2.5 Có tính trì hoãn
Tính toán trì hoãn (Lazy Evaluation) trong lập trình hàm giúp trì hoãn việc tính toán một biểu thức cho đến khi giá trị của biểu thức đó thực sự cần thiết (được gọi ở đâu đó), giúp tối ưu hóa hiệu suất và tiết kiệm tài nguyên.
Ví dụ: Trong Python, generator không tính toán tất cả các giá trị ngay lập tức, mà chỉ tạo ra từng giá trị khi cần.
def lazy_range(n):
for i in range(n):
yield i # Chỉ trả về giá trị khi được yêu cầu
# Sử dụng generator
gen = lazy_range(5)
# Chỉ khi bạn yêu cầu giá trị, tính toán mới được thực hiện
for value in gen:
print(value)
2.6 Cho phép Kết hợp hàm
Kết hợp hàm (Function Composition) cho phép bạn tạo ra các hàm mới bằng cách kết hợp hai hoặc nhiều hàm với nhau. Thay vì thực thi các hàm riêng biệt, bạn có thể ghép chúng lại thành một chuỗi, trong đó đầu ra của hàm này trở thành đầu vào của hàm kia.
Giả sử bạn có hai hàm: multiply_by_2
(nhân một số với 2), add_3
(thêm 3 vào một số).
def multiply_by_2(x):
return x * 2
def add_3(x):
return x + 3
def multiply_and_add(x):
return add_3(multiply_by_2(x))
print(multiply_and_add(5)) # Kết quả: 13 (5 * 2 + 3)
3. Kết luận
Lập trình hàm (Functional Programming) là một phong cách lập trình tập trung vào các hàm thuần túy, tránh thay đổi trạng thái và loại bỏ tác dụng phụ, phù hợp cho các ứng dụng yêu cầu xử lý song song, phân tán, hoặc quản lý dữ liệu lớn.
Các bài viết liên quan tại Blog 200Lab:
Bài viết liên quan
Giới thiệu Kiến trúc Backend for Frontend (BFF)
Nov 16, 2024 • 10 min read
Flask là gì? Hướng dẫn tạo Ứng dụng Web với Flask
Nov 15, 2024 • 7 min read
Webhook là gì? So sánh Webhook và API
Nov 15, 2024 • 8 min read
Spring Boot là gì? Hướng dẫn Khởi tạo Project Spring Boot với Docker
Nov 14, 2024 • 6 min read
Two-Factor Authentication (2FA) là gì? Vì sao chỉ Mật khẩu thôi là chưa đủ?
Nov 13, 2024 • 7 min read
Test-Driven Development (TDD) là gì? Hướng dẫn thực hành TDD
Nov 13, 2024 • 6 min read