Functional Programming với Python

Lập trình hàm (Functional Programming) là gì?

Lập trình hàm là một trường phái trong đó coi hàm (không phải object) là các khối nền tảng để xây dựng chương trình, với ý tưởng ta có thể truyền hàm như là tham số tới các hàm khác và có thể trả về chúng như gía trị. Lập trình hàm liên quan đến việc viết code không thay đổi trạng thái. Lý do chính là các lời gọi hàm liên tiếp sẽ cho ra cùng kết qủa. Ta có thể dùng lập trình hàm với bất kỳ ngôn ngữ nào hỗ trợ first-class functions. Một số ngôn ngữ như Haskell, không cho phép thay đổi trạng thái (ví dụ: các thao tác vào ra).

Lợi thế lập trình hàm mang lại?

Functional programming giúp code ít lỗi hơn do mỗi thành phần đọc lập hoàn toàn với nhau. Hơn nữa, code sẽ dễ đọc hơn bởi vì cú pháp giống các biểu thức toán học.

Lập trình hàm với Python

Trong phần này, ta sẽ cũng làm quen với một vài functions cho phép lập trình theo phong cách functional trong ngôn ngữ Python. Code trong bài viết sử dụng với python 3.

Itertools

Hãy bắt đầu với khái niệm nền tảng quan trọng cho việc lập trình hàm trong python: iterators. Đây là đối tượng biểu diễn dòng dữ liệu và trả về từng phần tử một. Iterator cực kỳ hữu dụng khi thao tác với lists, tuples, strings, ..., etc. Các hàm cung cấp bởi itertools bắt chước các tính năng tương tự của các ngôn ngữ lập trình hàm như ClojureHaskell, được tối ưu về tốc độ và bộ nhớ.

chain()

Hàm chain() nhận đầu vào là các iterators và xâu chuỗi lại thành một iterator duy nhất.

from itertools import chain

l1 = [1, 2, 3]
l2 = [4, 5, 6]
l3 = [7, 8, 9]
for i in chain(l1, l2, l3):
    print(i) # 1,2,3,4,5,6,7,8,9

print(list(chain(l1, l2, l3))) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(sum(chain(l1, l2, l3))) # 45

zip()

Tạo ra một iterator kết hợp các phần tử từ iterators khác. zip() có đặc tính lazy, nghĩa là nó chỉ tính toán các phần tử khi được yêu cầu.

lst = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
for i in zip(*lst)
    print(i)
# (1, 4, 7)
# (2, 5, 8)
# (3, 6, 9)

islice()

Hàm này trả về một iterator theo cách thức tương tự như slice, iterable[start:stop:step]. Khác với slice, start, stop, step không được nhận gía trị âm.

from itertools import islice
a = range(10)
i = iter(a)
print(list(islice(i, 1, 3))
print(list(islice(i, 1, 3))
print(list(islice(i, 1, 3))
print(list(islice(i, 1, 3))
#[1, 2]
#[4, 5]
#[7, 8]
#[]

map()

Trả về một iterator cho ra các phần tử function(i1, i2, ..., iN), trong đó i1, i2, ..., iN là các phần tử được lấy ra từ các iterators iter1, iter2, ..., iterN tương ứng. Nếu functionNone, hàm sẽ trả về các tuples có dạng (i1, i2, ..., iN).

from operator import add

print([i for i in map(pow, (2,3,10), (5,2,3))])
# [32, 9, 1000]

print([i for i in map(add, map(pow, (2,3,10), (5,2,3)), (2,2,2))])
# [34, 11, 1002]

Comprehensions

List Comprehensions

Các bạn có thể tham khảo list comprehension tại bài viết này.

Set Comprehensions

Set comprehensions cho phép ta xây dựng tập hợp (sets) theo cách thức như list comprehension, chỉ khác rằng kết qủa trả về là đối tượng set thay vì list.

Gỉa sử từ list các tên bên dưới, ta muốn lọc ra các tên có độ dài lớn hơn 1 và biểu diễn tất cả các tên theo cũng định dạng: chỉ viết hoa chữa cái đầu tiên.

danh sách đầu vào:

names = ['Bob', 'JOHN', 'alice', 'bob', 'ALICE', 'J', 'Bob']

Set mà ta mong muốn:

{'Bob', 'John', 'Alice'}

Đoạn code sau sử dụng set comprehensions thực hiện yêu cầu trên:

names = ['Bob', 'JOHN', 'alice', 'bob', 'ALICE', 'J', 'Bob']
s = { name[0].upper() + name[1:].lower() for name in names if len(name) > 1 }
print(s)
# {'Bob', 'John, 'Alice'}

Dictionary Comprehensions

Gỉa sử ta có một dictionary với keys là các ký tự và gía trị là số lần ký tự đó xuất hiện trong một đoạn text. Hiện tại, dictionary đang phân biệt chữ hoa với chữ thường.

Ta muốn lấy về một dict trong đó chữ hoa và chữ thường được kết hợp với nhau.

dct = {'a': 10, 'b': 34, 'A': 7, 'Z': 3}
freq_dict = {k.lower() : dct.get(k.lower(), 0) + dct.get(k.upper(), 0) for k in dct.keys()}
print(freq_dict)
# {'a': 17, 'z': 3, 'b': 34}

Lambda

Python hỗ trợ tạo hàm động không tên hay còn gọi là anonymous functions, bằng cách sử dụng lambda. Tuy không đầy đủ như lambda trong các ngôn ngữ functional nhưng đây cũng là một kỹ thuật mạnh mẽ được tích hợp tốt trong Python và thường được sử dụng kết hợp với các khái niệm functional điển hình như filter(), map()reduce().

Cú pháp xậy dựng anonymous functions:

lambda args: expression

Trong đó, args là danh sách (được ngăn cách bởi dấu phẩy ",") các tham số và expression là một biểu thức theo các tham số trên. Đoạn code dưới đây chỉ ra sự khác nhau giữa một hàm thông thường và anonymous functions:

def square(x):
    return x*x
print(square(4)) # 16

#----------------------#
square = lambda x: x*x
print(square(4)) # 16

Như bạn thấy, cả 2 hàm square() cùng thực hiện một chức năng và lời gọi hàm là giống nhau. Lưu ý rằng lambda không chứa câu lệnh return - nó luôn chứa một expression được trả về. Hơn nữa, bạn có thể đặt lambda functions bất cứ nơi đâu mà bạn muốn, không cần nhất thiết phải gán tên cho nó.

Kết luận

Hy vọng bài viết mang lại cho bạn những kiến thức hữu ích về lập trình hàm trong Python. Bài viết này được lược dịch từ link nguồn bên dưới.

Nguồn: http://www.pysnap.com/functional-programming-in-python/