Lập trình hàm (functional programming) trong Python – phần 1
Nội dung của bài
Lập trình hàm( functional programming ) là gì?
Trong khoa học máy tính, lập trình hàm là một mô hình lập trình trong đó các chương trình được xây dựng bằng cách sử dụng các hàm để biểu diễn mối quan hệ giữa các đối tượng giống như trong toán học. Với ý tưởng sử dụng hàm như là một tham số của hàm khác, và hàm cũng là giá trị được trả về.
Functional Programming sử dụng các các biểu thức và khai báo thay vì thực thi các câu lệnh. Chính vì vậy, cách lập trình này hoàn toàn khác với các thủ tục khác thường dựa vào những trạng thái cục bộ hoặc toàn cục. Giá trị đầu ra của Functional Programming chỉ phụ thuộc vào các tham số được truyền cho hàm.
Một số ngôn ngữ Functional Programming nổi bật bao gồm: Haskell, SML, Clojure, Scala, Erlang, Clean, F#, Mathematica,… Một số ngôn ngữ nổi tiếng khác như JavaScript hay Python cho phép người dùng tùy chọn. Bạn vừa có thể sử dụng OOP hoặc phương pháp lập trình hàm, tùy vào mục đích của mình.
Đặc điểm của lập trình hàm( functional programming )
- Dữ liệu là bất biến, không thay đổi.
- Được xây dựng dựa trên khái niệm về các hàm toán học. Cụ thể, lập trình hàm sử dụng các biểu thức điều kiện và đệ quy để thực hiện phép tính.
Lập trình hàm ( functional programming) trong Python
Không cần phải sử dụng thư viện nào chúng ta cũng có thể triển khai lập trình hàm với ngôn ngữ python.
Pure Functions
Pure Function là một hàm mà với một input chúng ta luôn luôn có 1 output duy nhất. Giá trị của Input không bị thay đổi hay phụ thuộc vào biến khác ở ngoài hàm. Điều này giúp cho hàm của chúng ta dễ test hơn.
Chúng ta cùng xem ví dụ sau đây:
def multiply_3_pure(numbers):
multiply_numbers = []
for n in numbers:
multiply_numbers.append(n * 3)
return multiply_numbers
original_numbers = [2, 4, 8]
changed_numbers = multiply_3_pure(original_numbers)
print(original_numbers) # [2, 4, 8]
print(changed_numbers) # [6, 12, 24]
sau lời gọi hàm danh sách original_numbers không thay đổi. Chúng ta cũng không tham chiếu đến bất kỳ biến nào khác nằm ngoài phạm vi của hàm. Đây chính là một Pure Function.
Immutability
Hiểu một cách đơn giản thì Immutability là bất biến là không có khả năng thay đổi. Hẳn bạn đã từng làm gặp trường hợp giá trị một biến ban đầu được khai báo là 100, nhưng sau một dòng code thì giá trị đó đã được thanh đổi thành None chẳng hạn. Điều này khiến chúng ta khó kiểm soát chương trình và có thể gây ra lỗi.
Hãy cùng xem ví dụ dưới đây:
mutable_collection = ['Uncle Bob', 7, [50, 100]]
immutable_collection = ('Uncle Bob', 10, [50, 100])
print(mutable_collection[2]) # [50, 100]
print(immutable_collection[2]) # [50, 70]
mutable_collection[1] = 100
print(mutable_collection) #['Uncle Bob', 100, [50, 100]]
immutable_collection[1] = 15 # TypeError: 'tuple' object does not support item assignment
Dòng code: immutable_collection[1] = 15 sẽ gây ra lỗi: TypeError: ‘tuple’ object does not support item assignment.
Vì kiểu dữ liệu tuple là không thể thay đổi, các bạn có thể đọc thêm bài viết kiểu dữ liệu tuple.
Một điều thú ví là, chúng ta không thể thay đổi giá trị của tuple nhưng lại có thể thay đổi giá trị của phần tử bên trong nó. Đó là lý do vì sao mình lại lấy ví dụ về giá trị ban đầu của immutable_collection = (‘Uncle Bob’, 10, [50, 100]). Giờ tao có thể thay đổi danh sách (list) [50, 100] trong tuple immutable_collection thành [50, 100, 150]:
immutable_collection[2].append(150)
print(immutable_collection[2]) # [50, 100, 150]
Lý do chúng ta có thể thực hiện điều trên vì list là một mutable object.
Bây giờ chúng ta thử thay đổi giá trị của danh sách trong tuple immutable_collection thành giá trị ban đầu [50, 100] bằng cách:
immutable_collection[2] = [50, 100] # TypeError: 'tuple' object does not support item assignment
Dòng lệnh trên sẽ gây ra lỗi: TypeError: ‘tuple’ object does not support item assignment. Lý do là chúng ta không thể tham chiếu đến một mutable object khác đã được lưu trữ trong bộ nhớ.
Higher Order Functions
Higher Order Functions là một function có đầu vào là một hàm hoặc đầu ra cũng là một hàm. Hãy xem ví dụ dưới đây:
def write_messsage(msg, times):
for i in range(times):
print(msg)
write_message("Hello! I am Uncle Bob!", 5)
Giả sử bây giờ chúng ta muốn thêm một phương thức dùng log thay cho hàm print, chúng ta có thể viết một hàm mới hoặc dùng Higher Order Functions:
def write_messsage(msg, times, action):
import logging
def write_messsage(msg, times, action):
for i in range(times):
action(msg)
write_messsage("Hello! I am Uncle Bob!", 2, print)
# Hello! I am Uncle Bob!
# Hello! I am Uncle Bob!
write_messsage("Hello! I am Uncle Bob!", 2, logging.error)
# ERROR:root:Hello! I am Uncle Bob!
# ERROR:root:Hello! I am Uncle Bob!
Chúng ta tìm hiểu thêm một ví dụ một Higher Order Function có đầu ra là một hàm.
def add_numbers(increment):
def add_increment(numbers):
new_numbers = []
for n in numbers:
new_numbers.append(n + increment)
return new_numbers
return add_increment
add_10 = add_numbers(10)
add_15 = add_numbers(15)
print(add_10([10, 30])) # [20, 40]
print(add_15([10, 30])) # [25, 45]
Higher Order Functions giúp code của chúng ta linh hoạt hơn. Với tính trừu tượng do việc có thể sử dụng hàm khác là đầu vào hoặc đầu ra giúp chúng ta dễ đàng kiểm soát hành vi của chương trình.
Kết luận
Functional Programming khá khó tiếp nhận, và áp dụng thuần thục. Hãy cùng nhau tìm hiểu ở phần 2 nhé.