Trong bài viết này, chúng ta sẽ tìm hiểu khái niệm về lập trình hàm, bao gồm cả sự khác biệt của nó với lập trình hướng đối tượng
Bài viết này được chia thành các phần sau
Giới thiệu về lập trình chức năng
Lập trình hàm là một mô hình lập trình trong đó mã được cấu trúc chủ yếu dưới dạng các hàm. Nguồn gốc của phong cách lập trình này phát sinh từ một nhánh toán học được gọi là phép tính lambda, là nghiên cứu về các hàm và tính chất toán học của chúng. Trái ngược với các cách tiếp cận thủ tục và hướng đối tượng phổ biến, lập trình hàm cung cấp một cách suy nghĩ khác khi giải quyết vấn đề
Hãy khám phá cách lập trình các giải pháp theo kiểu chức năng bằng Python
chức năng so với. lập trình hướng đối tượng
Trong lập trình hướng đối tượng [OOP], bạn cấu trúc mã của mình dựa trên khái niệm về đối tượng. Trong các ngôn ngữ OOP thường được sử dụng [chẳng hạn như Java hoặc C#], chúng tôi tạo mẫu cho một đối tượng bằng cách sử dụng từ khóa class
và chúng tôi khởi tạo một đối tượng bằng cách sử dụng từ khóa new
. Các đối tượng này chứa các trường lưu trữ dữ liệu và các phương thức thao tác dữ liệu. Trong phong cách này, một đối tượng có thể được truyền dưới dạng đối số trong hàm của một đối tượng khác
Trong lập trình chức năng, bạn tạo một giải pháp bằng cách cấu trúc mã chủ yếu sử dụng các chức năng. Các hàm là “công dân hạng nhất” trong mô hình này, nghĩa là chúng ta có thể sử dụng chúng để lưu trữ và thao tác dữ liệu [có giới hạn]. Tương tự như các đối tượng trong lập trình hướng đối tượng, trong lập trình hàm, các hàm có thể được truyền vào dưới dạng đối số cho các hàm khác và có thể được trả về bởi các hàm khác
Tuyên bố vs. lập trình bắt buộc
Trước khi tìm hiểu về những lợi thế mà lập trình chức năng mang lại, trước tiên chúng ta phải thảo luận về sự khác biệt giữa mô hình bắt buộc và mô hình khai báo
Lập trình mệnh lệnh giải quyết vấn đề bằng cách mô tả giải pháp từng bước. Lập trình mệnh lệnh liên quan đến “làm thế nào để giải quyết một vấn đề. ”
Ngược lại, lập trình khai báo dựa trên khung hoặc ngôn ngữ lập trình cơ bản để giải quyết vấn đề. Nhiệm vụ duy nhất của lập trình viên là mô tả vấn đề họ muốn giải quyết. Lập trình khai báo liên quan đến vấn đề “giải quyết vấn đề gì. ”
Tương tự như vậy, hãy xem xét ví dụ về việc lấy một tách cà phê. Lập trình bắt buộc sẽ tương đương với việc bạn tự pha một tách cà phê bằng cách thực hiện các bước sau
- Lấy bình cà phê của bạn ra
- Cho bã cà phê vào nồi
- Thêm nước vào bình cà phê
- Đặt ấm cà phê lên bếp
- Bật bếp
- Chờ cho đến khi cà phê được thực hiện
- Rót cà phê vào cốc
- Bạn có thích đường không?
- Nếu có, thêm đường
- Nếu không, không thêm đường
- Uống cà phê
Lập trình khai báo tương đương với việc bạn đến quán cà phê gần nhà và nói với nhân viên pha chế, “Tôi muốn một tách cà phê với một thìa cà phê đường, vui lòng. ” Nhân viên pha cà phê chuẩn bị cà phê cho bạn, đưa cho bạn tách cà phê và bạn uống nó mà không cần quan tâm đến chi tiết nó được pha như thế nào
Đối với một phép tương tự trong lập trình, hãy xem xét ví dụ về việc phải sắp xếp một danh sách trong Python. Việc sắp xếp danh sách theo cách bắt buộc sẽ là bạn thực hiện một thuật toán sắp xếp như thế này
lst = [8, 4, 14, 9, 12, 5, 7, 1, 10, 2, 3]
for j in range[i+1, len[lst]]:
min_idx = j if lst[j] < lst[min_idx] else min_idx
Nếu chúng ta giải quyết danh sách theo cách khai báo bằng Python, chúng ta sẽ gọi hàm
list.sort[]
như vậylst = [8, 4, 14, 9, 12, 5, 7, 1, 10, 2, 3]
Lập trình viên theo phong cách nào có thể phụ thuộc vào mục tiêu của họ. Nếu bạn đang tìm kiếm mã ngắn gọn giúp bạn có câu trả lời hiệu quả, bạn có thể sử dụng cách tiếp cận mang tính khai báo hơn. Nếu bạn đang làm việc với những người khác và muốn đảm bảo rằng mã của bạn dễ theo dõi, bạn có thể chọn kiểu bắt buộc hơn
OOP và lập trình thủ tục tuân theo một cách tiếp cận bắt buộc;
Viết hàm trong lập trình hàm
Như chúng tôi đã nói trong phần giới thiệu của bài viết này, khái niệm trung tâm trong lập trình hàm là hàm; . Yêu cầu đầu tiên là chức năng phải xác định. Nghĩa là, đối với bất kỳ đầu vào cụ thể nào, hàm phải trả về cùng một đầu ra khi được cung cấp cùng một bộ đầu vào. Hãy minh họa điều này bằng ví dụ sau về hàm
add
Hàm này sẽ luôn trả về
7
nếuadd[5, 2]
được gọiMột trong những yêu cầu này là các chức năng phải không có càng nhiều tác dụng phụ càng tốt. Tác dụng phụ là khi một chức năng thay đổi một số biến bên ngoài. Mục tiêu là để giảm thiểu, không loại bỏ, tác dụng phụ. Sẽ không thể loại bỏ tất cả các hiệu ứng phụ trong một chức năng bởi vì một chương trình cuối cùng phải có hiệu ứng bên ngoài để hữu ích
Một hàm thuần túy được định nghĩa là một hàm xác định không có tác dụng phụ
Hãy xem xét ví dụ này
Hàm
add[x, y]
được xác định theo cách này có tác dụng phụ vì nó thay đổi biến ngoài0lst = [8, 4, 14, 9, 12, 5, 7, 1, 10, 2, 3]
Để biến đây thành một hàm thuần túy, chúng ta nên viết nó dưới dạng
Lưu ý cách hàm
add[x, y]
không còn thay đổi biến0 trong khi thực thi. Ngoài ra, các biếnlst = [8, 4, 14, 9, 12, 5, 7, 1, 10, 2, 3]
3 vàlst = [8, 4, 14, 9, 12, 5, 7, 1, 10, 2, 3]
4 chỉ tồn tại trong phạm vi của hàm. Hoạt động trên chúng cho phép chức năng không có tác dụng phụlst = [8, 4, 14, 9, 12, 5, 7, 1, 10, 2, 3]
Để có thể sử dụng lại, một hàm phải luôn có tất cả các tham số liên quan được truyền vào và không nên dựa vào các biến toàn cục
Xem xét ví dụ về hàm [
5] thay thế phần tử đầu tiên và cuối cùng của danh sách bằnglst = [8, 4, 14, 9, 12, 5, 7, 1, 10, 2, 3]
6lst = [8, 4, 14, 9, 12, 5, 7, 1, 10, 2, 3]
Viết một hàm theo cách này khiến nó phụ thuộc vào một biến danh sách bên ngoài có tên là
7. Không thể sử dụng lại chức năng này vì sử dụng nó ở nơi khác yêu cầu phải xác định bên ngoàilst = [8, 4, 14, 9, 12, 5, 7, 1, 10, 2, 3]
7. Mặc dù nó không thể tái sử dụng, nhưng biếnlst = [8, 4, 14, 9, 12, 5, 7, 1, 10, 2, 3]
5 vẫn được coi là không có tác dụng phụ khi nó đọc, nhưng không sửa đổi, biếnlst = [8, 4, 14, 9, 12, 5, 7, 1, 10, 2, 3]
7. Một cách tốt hơn để viết hàm là chuyển vào biến danh sách được yêu cầu dưới dạng đối số như vậylst = [8, 4, 14, 9, 12, 5, 7, 1, 10, 2, 3]
Chức năng
1 không có tác dụng phụ và hiện cũng có thể tái sử dụngdef recursive_function[n]:
Chúng tôi quan tâm đến các hàm thuần túy vì khi các chương trình của chúng tôi trở nên phức tạp hơn, chúng tôi muốn tránh những thay đổi không chính xác của các biến bên ngoài. Ví dụ: nếu chúng ta gọi các hàm từ các luồng khác nhau đang chạy đồng thời, chúng ta cần xây dựng các hàm của mình một cách cẩn thận để tránh tác dụng phụ. Theo mô hình lập trình chức năng là một cách tuyệt vời để giữ cho mã sạch sẽ, hiệu quả và đơn giản hóa việc kiểm tra
Sử dụng đệ quy thay vì vòng lặp
Để thực thi lặp đi lặp lại một đoạn mã, các lập trình viên hướng đối tượng sử dụng vòng lặp
2 hoặc vòng lặpdef recursive_function[n]:
3. Các vòng lặp không tuân thủ kiểu lập trình chức năng vì chúng duy trì một bộ đếm bên ngoài được thay đổi từ bên trong vòng lặp [tác dụng phụ. ]. Trong lập trình chức năng, chúng tôi sử dụng đệ quy để thực thi mã lặp đi lặp lại. Các hàm đệ quy thường được viết theo kiểu saudef recursive_function[n]:
def recursive_function[n]:
Ví dụ cổ điển về hàm đệ quy là tính toán số Fibonacci thứ n được thực hiện như vậy
Truyền chức năng làm đối số cho các chức năng khác
Trong OOP truyền thống, bạn có thể truyền đối tượng làm đối số cho hàm. Trong lập trình hàm, bạn có thể chuyển các hàm làm đối số cho các hàm khác. Điều này được gọi là coi các chức năng là công dân hạng nhất, một thuật ngữ bạn có thể thường thấy trong ngành
Hãy xem một ví dụ
Mã này định nghĩa một hàm
4 nhân kết quả của một hàmdef recursive_function[n]:
add
hoặc hàm trừ [_______12_______6] với ba và trả về giá trị. Hàm4 cũng cho phép truyền bất kỳ hàm nào khác chấp nhận hai tham số và trả về một số nguyêndef recursive_function[n]:
Lập trình chức năng thúc đẩy khái niệm ngắn gọn khi viết mã. Điều quan trọng là phải ngắn gọn và viết các hàm trong càng ít dòng càng tốt miễn là vẫn duy trì được khả năng đọc. Chúng ta cũng có thể truyền một hàm cho một hàm khác bằng cách viết nó trong đối số bằng lambda
Chúng ta có thể rút ngắn mã trước đó bằng lambda như vậy
Điều này sẽ đưa ra kết quả tương tự như việc thực hiện dài dòng từ trước đó. Lambdas là một công cụ tuyệt vời vì chúng cho phép lập trình viên viết một hàm trong khi vẫn duy trì luồng ý tưởng. Một lập trình viên không còn phải dừng lại, viết một chức năng ở nơi khác và sau đó tiếp tục công việc. Một nhược điểm của lambdas là chúng chỉ phù hợp để thực hiện các chức năng đơn giản;
Phần kết luận
Tóm lại, lập trình hàm là một mô hình mạnh mẽ mang lại nhiều lợi thế. Trong mô hình này, chúng tôi cấu trúc các giải pháp dưới dạng các chức năng. Lập trình hàm khác với lập trình hướng đối tượng. Nó tuân theo kiểu khai báo trái ngược với kiểu mệnh lệnh dẫn đến mã ngắn hơn, dễ kiểm tra hơn, ít bị lỗi hơn và rất phù hợp cho các ứng dụng đa luồng. Nhiều ngôn ngữ cho phép triển khai mô hình lập trình chức năng;
Điều gì tạo nên một ngôn ngữ lập trình chức năng?
Lập trình hàm tìm cách tận dụng hỗ trợ ngôn ngữ trong việc sử dụng hàm làm biến, đối số và giá trị trả về để tạo mã đẹp . Bởi vì các hàm hạng nhất rất linh hoạt và hữu ích, ngay cả các ngôn ngữ OOP mạnh mẽ như Java và C# cũng đã chuyển sang kết hợp hỗ trợ hàm hạng nhất.Python có chức năng hay thủ tục không?
Python là cả thủ tục và có các tính năng hướng đối tượng, cũng như một số khía cạnh của lập trình chức năng.Python là ngôn ngữ chức năng hay mệnh lệnh?
Python thường được mã hóa theo cách bắt buộc nhưng có thể sử dụng kiểu khai báo nếu cần. Một số tính năng của Python bị ảnh hưởng bởi Haskell, một ngôn ngữ lập trình chức năng thuần túy.Ưu điểm của lập trình chức năng trong Python là gì?
Mục đích của các mô hình lập trình khác nhau không phải là biến những thứ trước đây không thể thành có thể, mà là làm cho mọi thứ trở nên dễ dàng mà trước đây khó khăn. Lập trình hàm nhằm mục đích giúp bạn dễ dàng viết các chương trình ngắn gọn, không có lỗi và có thể song song hóa