Map và filter trong python
Một trong những bộ ba function cực chất mà hầu như ngôn ngữ nào cũng phải có giúp cho việc xử lý mảng trở lên nhanh gọn hơn rất nhiều. đó chính là bộ 3 Show Hàm
Trong đó:
Hàm VD: nhân đôi giá trị của tất cả các phần tử trong list.
Và
VD:
2, filter().Hàm này cũng có tác dụng duyệt qua các phần tử trong list, dict,... nhưng khác với map là hàm này sẽ chỉ trả về những giá trị mà điều kiện trong function chấp nhận (có nghĩa là True). Cú pháp:
Trong đó:
Hàm VD:
3, Lời kết.Bằng việc sử dụng lambda trong Map, filter và reduce là ba hàm đặc thù của lập trình hàm. Đặc điểm chung của chúng là cho phép áp dụng một hàm xử lý trên một danh sách dữ liệu: map áp dụng một hàm trên dữ liệu của một danh sách; filter áp dụng hàm cho danh sách và chỉ lấy những kết quả phù hợp điều kiện; reduce biến một danh sách về một giá trị. Ba loại hàm đặc biệt này cho phép viết code xử lý dữ liệu ngắn gọn, xúc tích, hiệu quả hơn mà không cần đến các kỹ thuật lập trình thông thường với vòng lặp và rẽ nhánh. Imperative và functionalCùng xuất phát với một ví dụ đơn giản nhất. Giả sử bạn đang có một danh sách X chứa các giá trị như sau: X = [x/100 for x in range(100, 1000)] #dải giá trị [1, 10] với bước 0,01 Bạn muốn tạo ra một danh sách khác, Y, trong đó mỗi phần tử của Theo lối suy nghĩ thông thường, bạn sẽ:
“Thuật toán” trên được minh họa qua code Python như sau: X = [x/100 for x in range(100, 1000)] Y = [] def parabolic(x, a=1.0, b=0.0, c=0.0): return a * x * x + b * x + c for x in X: y = parabolic(x) Y.append(y) Lối lập trình này được gọi là lập trình mệnh lệnh (imperative programming). Lối lập trình này không sai nhưng dài dòng và thủ công. Bạn phải tự mình quản từng bước trong quá trình thực hiện thuật toán đặt ra. Trong các bài học trước bạn đã biết về các cấu trúc dữ liệu dạng container như list, touple, dict. Bạn cũng học cách xây dựng các container riêng qua bài học về iterable/iterator và generator trong Python. Bạn có thể đã thắc mắc, tại sao Python lại phải tạo các cấu trúc rắc rối như iterator? Trên thực tế, đó là các cấu trúc dữ liệu rất mạnh để sử dụng trong lập trình hàm (functional programming) – một xu hướng lập trình rất mạnh trong xử lý dữ liệu. Map, filter và reduce là ba loại hàm thông dụng trong xu hướng này. Sử dụng hàm map() trong PythonGiờ hãy thay thế toàn bộ code ở trên như sau: X = [x / 100 for x in range(100, 1000)] def parabolic(x, a=1.0, b=0.0, c=0.0): return a * x * x + b * x + c Y = list(map(parabolic, X)) for y in Y: print(y, end=' ') Đoạn code mới vẫn giữ lại phần khai báo X và hàm parabolic nhưng phần tính toán Y đã hoàn toàn thay đổi. Chúng ta sử dụng hàm map() thay cho vòng lặp for và chuyển đổi kết quả về dạng danh sách qua hàm list(). Để ý cách sử dụng hàm map(): Do hàm parabolic đòi hỏi 1 tham số, bạn chỉ cần truyền 1 iterable cho hàm map(). Nếu hàm yêu cầu nhiều hơn 1 tham số, bạn cần cung cấp số lượng iterable tương ứng. Ví dụ, nếu hàm cần 2 tham số, bạn phải cung cấp 2 iterable. Trong mỗi lượt thực hiện, map() sẽ lấy từ mỗi iterable một phần tử. Về ý nghĩa, sử dụng hàm map() là tương tự như phiên bản imperative bạn đã thấy ở ví dụ 1: (1) lần lượt duyệt qua từng phần tử x của X; (2) ứng với mỗi phần tử x áp dụng hàm parabolic để thu được kết quả y; (3) thêm giá trị y vào object kết quả cuối cùng. Sự khác biệt nằm ở chỗ: (1) quá trình được Python thực hiện tự động; (2) kết quả cuối cùng là một object kiểu map chứ không phải list. Do vậy bạn cần chuyển đổi từ kiểu map về kiểu list hoặc kiểu khác (nếu cần). map() của Python tương tự như LINQ Select của C#. Kiểu map cũng đồng thời là một generator nên bạn cũng có thể sử dụng nó trong vòng lặp for: for y in Y: print(y, end=' ') Giá trị của y cùng kiểu với kiểu trả về của hàm parabolic (float). Khi sử dụng map() cần lưu ý:
*Một đặc thù của lập trình hàm là mọi hàm phải trả về kết quả. Trong Python, nếu hàm không có return, Python sẽ coi kết quả trả về của hàm là Hàm filter() trong PythonHãy bắt đầu bằng một ví dụ: class Person: def __init__(self, name, age): self.name = name self.age = age def print(self): print(f'{self.name} ({self.age} years old)') @staticmethod def elder(person): if person.age >= 75: return True return False presidents = [Person('Putin', 65), Person('Trump', 80), Person('Obama', 70), Person('Bush', 75), Person('Clinton', 55), Person('Yeltsin', 65), Person('Nixon', 90)] def elder(person): if person.age >= 75: return True return False if __name__ == '__main__': old_presidents = filter(Person.elder, presidents) for p in old_presidents: p.print() Chạy chương trình cho kết quả: Trump (80 years old) Bush (75 years old) Nixon (90 years old) Trong ví dụ trên chúng ta xây dựng một class đơn giản Person với hai attribute name và age. Trong class này chúng ta định nghĩa static method elder. Phương thức này trả về giá trị True nếu age >= 75. Để thử nghiệm, chúng ta tạo danh sách presidents chứa các object thuộc kiểu Person. Yêu cầu đặt ra là cần lọc danh sách presidents để lấy ra những người từ 75 tuổi trở lên. Theo logic thông thường (imperative), bạn sẽ dùng vòng lặp for trên presidents. Ứng với mỗi phần tử bạn áp dụng hàm elder() hoặc phương thức Person.elder(). Nếu thu được kết quả True, bạn sẽ đưa object tương ứng vào danh sách kết quả. Thay vì làm thủ công như trên, trong Python bạn có thể áp dụng giải pháp của lập trình hàm với hàm filter(): Cách sử dụng hàm filter() rất giống với hàm map() bạn đã làm quen ở phần trước. Tuy nhiên có vài điểm sau cần lưu ý:
Hàm filter() của Python hoạt động giống như phương thức Where của C# LINQ. Hàm reduce() trong PythonTrước hết hãy xem ví dụ về hàm tính giai thừa của một số: from functools import reduce # imperative factorial function def imp_fact(n: int): p = 1 for i in range(1, n + 1): p *= i return p # functional factorial function def fun_fact(n: int): def mult(x, y): return x * y return reduce(mult, range(1, n + 1)) def test1(): n = 3 f = imp_fact(n) print(f'{n}! = {f}') def test2(): n = 3 f = fun_fact(n) print(f'{n}! = {f}') if __name__ == '__main__': test1() test2() Trong ví dụ này chúng ta xây dựng hai hàm tính giai thừa, một hàm theo kiểu imperative (imp_fact) và một hàm theo kiểu functional (fun_fact). Để ý trong hàm imp_fact, ở vòng lặp for chúng ta thực hiện phép toán nhân dồn (cumulative) các số liên tiếp trong khoảng [1, n+1) cho p để thu được giá trị giai thừa theo định nghĩa của phép toán này. Phép
toán nhân dồn (và các phép dồn tương tự) được thực hiện bởi hàm reduce() trong fun_fact(): Khác biệt với map() và filter(), hàm reduce() không phải là hàm built-in. Để sử dụng hàm reduce(), bạn phải import nó từ package functools: Cách hoạt động của reduce với mult và range(1, n+1) trong ví dụ trên như sau:
Cách thức hoạt động của reduce hoàn toàn tương tự như cách chúng ta sử dụng phép nhân dồn trong vòng for bình thường: # nhân dồn với vòng lặp for i in range(1, n+1): p = p * i # hay p *= i Khi sử dụng reduce() cần lưu ý:
Sử dụng hàm lambdaTrong các ví dụ trên bạn có thể thấy, map(), filter() và reduce() đều nhận một hàm khác làm tham số thứ nhất. Do vậy, bạn đều phải định nghĩa hàm tương ứng. Tuy nhiên, trong rất nhiều trường hợp các hàm này đều đơn giản, thường chỉ có 1 biểu thức duy nhất, ví dụ như Việc định nghĩa hàng loạt hàm “mini” sử dụng một lần như vậy làm code khó theo dõi. Python cho phép định nghĩa trực tiếp hàm ở nơi cần dùng hàm tham số với hàm lambda. Hãy cùng làm lại các ví dụ ở các phần trên: def fun_fact(n: int): # sử dụng hàm lambda x, y: x *y return reduce(lambda x, y: x * y, range(1, n + 1)) # sử dụng hàm lambda p : True if p.age >= 75 else False old_presidents = filter(lambda p: True if p.age >= 75 else False, presidents) # sử dụng hàm lambda x, a=1.0, b=0.0, c=0.0: a * x * x + b * x + c Y = map(lambda x, a=1.0, b=0.0, c=0.0: a * x * x + b * x + c, X) Trong đoạn code trên chúng ta đã sử dụng một loạt hàm lambda: lambda x, y: x *y lambda p : True if p.age >= 75 else False lambda x, a=1.0, b=0.0, c=0.0: a * x * x + b * x + c Hàm lambda là những hàm không có tên và thân chỉ chứa 1 biểu thức duy nhất. Cú
pháp chung của hàm lambda là: lambda Trong đó, lambda là từ khóa bắt buộc. Danh sách tham số tương tự như của một hàm thông thường. Biểu thức kết quả không cần từ khóa return. Kết quả tính của biểu thức kết quả chính là kết quả thực hiện hàm lambda tương ứng. Hàm lambda có thể được khai báo trực tiếp ở nơi cần dùng, do vậy tránh được nhu cầu xây dựng các hàm mini sử dụng một lần. Kết luậnTrong bài học này bạn đã làm quen với một số hàm thông dụng trong lập trình hàm:
+ Nếu bạn thấy site hữu ích, trước khi rời đi hãy giúp đỡ site bằng một hành động nhỏ để site có thể phát triển và phục vụ bạn tốt hơn. |