Tại sao Python không tốt cho lập trình chức năng? [đóng cửa]


324

Tôi đã luôn nghĩ rằng lập trình chức năng có thể được thực hiện bằng Python. Vì vậy, tôi đã rất ngạc nhiên khi Python không được nhắc đến nhiều trong câu hỏi này và khi được đề cập, nó thường không tích cực lắm. Tuy nhiên, không có nhiều lý do được đưa ra cho điều này (thiếu kết hợp mẫu và dữ liệu đại số đã được đề cập). Vì vậy, câu hỏi của tôi là: tại sao Python không tốt cho lập trình chức năng? Có nhiều lý do hơn việc nó không phù hợp với kiểu dữ liệu và kiểu dữ liệu đại số không? Hay những khái niệm này rất quan trọng đối với lập trình chức năng đến nỗi một ngôn ngữ không hỗ trợ chúng chỉ có thể được phân loại là ngôn ngữ lập trình chức năng hạng hai? (Hãy nhớ rằng kinh nghiệm của tôi với lập trình chức năng là khá hạn chế.)


2
2018 - Coconut (một ngôn ngữ lập trình chức năng biên dịch thành Python) tăng cường lập trình chức năng trong Python. Xem thêm loạt bài viết này từ IBM page1 page2 page3
cssyphus 16/11/18

Câu trả lời:


393

Câu hỏi bạn tham khảo hỏi ngôn ngữ nào thúc đẩy cả OO và lập trình chức năng. Python không thúc đẩy lập trình chức năng mặc dù nó hoạt động khá tốt.

Lập luận tốt nhất chống lại lập trình chức năng trong Python là các trường hợp sử dụng bắt buộc / OO được Guido xem xét cẩn thận, trong khi các trường hợp sử dụng lập trình chức năng thì không. Khi tôi viết Python bắt buộc, đó là một trong những ngôn ngữ đẹp nhất mà tôi biết. Khi tôi viết Python chức năng, nó trở nên xấu xí và khó chịu như ngôn ngữ trung bình của bạn không có BDFL .

Điều đó không có nghĩa là nó tệ, chỉ là bạn phải làm việc chăm chỉ hơn bạn nếu bạn chuyển sang ngôn ngữ thúc đẩy lập trình chức năng hoặc chuyển sang viết OO Python.

Dưới đây là những điều chức năng tôi bỏ lỡ trong Python:


  • Không khớp mẫu và không có đệ quy đuôi có nghĩa là các thuật toán cơ bản của bạn phải được viết một cách bắt buộc. Đệ quy là xấu và chậm trong Python.
  • Một thư viện danh sách nhỏ và không có từ điển chức năng có nghĩa là bạn phải tự viết rất nhiều thứ.
  • Không có cú pháp cho currying hoặc bố cục có nghĩa là phong cách không có điểm có đầy dấu chấm câu như các đối số chuyển rõ ràng.
  • Lặp đi lặp lại thay vì danh sách lười biếng có nghĩa là bạn phải biết liệu bạn muốn hiệu quả hay kiên trì và phân tán các cuộc gọi đến listxung quanh nếu bạn muốn kiên trì. (Trình vòng lặp được sử dụng một lần)
  • Cú pháp mệnh lệnh đơn giản của Python, cùng với trình phân tích cú pháp LL1 đơn giản của nó, có nghĩa là một cú pháp tốt hơn cho biểu thức if và biểu thức lambda về cơ bản là không thể. Guido thích nó theo cách này, và tôi nghĩ anh ấy đúng.

5
+1 cho đệ quy đuôi bị thiếu - mặc dù các cấu trúc lặp đã thay thế nó, nhưng vẫn còn một thứ đáng để bỏ qua giữa Python và Scheme.
new123456

5
Câu trả lời tuyệt vời như cho sự đầy đủ và thành phần. Than ôi, giống như rất nhiều câu trả lời với một nền tảng chức năng mạnh mẽ, nó đã sử dụng thuật ngữ lạm dụng IMO. Mặc dù tôi hiểu rằng bạn không thể giải thích từng khái niệm trong một câu trả lời, tôi tự hỏi liệu OP (với nền FP giới hạn được thừa nhận) có được thông tin đầy đủ khi đọc các thuật ngữ như "khớp mẫu", "từ điển chức năng", "currying" hay " danh sách lười biếng ".
ThomasH

4
Điểm tốt; Tôi nghĩ rằng giải pháp là thêm liên kết. Bạn có đủ đại diện để chỉnh sửa câu trả lời của tôi không? Nếu vậy hãy thoải mái thêm các liên kết đến các khái niệm khác nhau. Tôi sẽ bắt đầu khi tôi có thời gian sau này.
Nathan Shively-Sanders

5
Tôi nhận ra đây là 5 tuổi, nhưng điều này có vẻ giống như những điều bạn nhớ từ Haskell , không phải từ các ngôn ngữ chức năng . Ví dụ, hầu hết các phương ngữ và hậu duệ ML và Lisp không có chế độ tự động, tạo kiểu không có điểm quá dài, không có danh sách lười biếng, v.v. không phải làm cho CaML trở thành một ngôn ngữ chức năng khủng khiếp ?
abarnert

4
@abarnert: Caml có mọi điểm gạch đầu dòng trừ các danh sách lười biếng, có sẵn dưới dạng thư viện. Thỉnh thoảng tôi sử dụng Caml tại thời điểm tôi viết câu trả lời này và hiện đang sử dụng F #. Cả hai đều là ngôn ngữ chức năng rất tốt đẹp.
Nathan Shively-Sanders

102

Guido có một lời giải thích tốt về điều này ở đây . Đây là phần có liên quan nhất:

Tôi chưa bao giờ coi Python bị ảnh hưởng nặng nề bởi các ngôn ngữ chức năng, bất kể mọi người nói hay nghĩ gì. Tôi đã quen thuộc hơn nhiều với các ngôn ngữ bắt buộc như C và Algol 68 và mặc dù tôi đã tạo ra các đối tượng hạng nhất, tôi không xem Python là ngôn ngữ lập trình chức năng. Tuy nhiên, trước đó, rõ ràng người dùng muốn làm nhiều hơn với danh sách và chức năng.

...

Điều đáng chú ý là mặc dù tôi đã không hình dung Python là ngôn ngữ chức năng, việc giới thiệu các bao đóng rất hữu ích trong việc phát triển nhiều tính năng lập trình nâng cao khác. Ví dụ, các khía cạnh nhất định của các lớp, trang trí kiểu mới và các tính năng hiện đại khác phụ thuộc vào khả năng này.

Cuối cùng, mặc dù một số tính năng lập trình chức năng đã được giới thiệu trong nhiều năm qua, Python vẫn thiếu một số tính năng nhất định được tìm thấy trong các ngôn ngữ lập trình chức năng của Real real. Chẳng hạn, Python không thực hiện một số loại tối ưu hóa nhất định (ví dụ: đệ quy đuôi). Nói chung, vì bản chất cực kỳ năng động của Python, không thể thực hiện loại tối ưu hóa thời gian biên dịch được biết đến từ các ngôn ngữ chức năng như Haskell hoặc ML. Và điều đó tốt.

Tôi rút ra hai điều trong số này:

  1. Người tạo ngôn ngữ không thực sự coi Python là ngôn ngữ chức năng. Do đó, có thể thấy các tính năng "chức năng-esque", nhưng bạn không thể thấy bất cứ điều gì có chức năng rõ ràng.
  2. Bản chất năng động của Python ức chế một số tối ưu hóa bạn thấy trong các ngôn ngữ chức năng khác. Cấp, Lisp cũng năng động (nếu không năng động hơn) như Python, vì vậy đây chỉ là một lời giải thích một phần.

8
Bạn có thể thực hiện tối ưu hóa cuộc gọi đuôi trong Python. Guido không / không hiểu điều đó.
Jules

26
Dường như Guido van Rossum không thích phong cách chức năng.
Svante

59
Tôi nghĩ chính xác hơn khi nói rằng Guido van Rossum không hiểu phong cách chức năng và không hiểu tại sao Python cần chúng. Bạn phải hiểu hai điều: 1) ngôn ngữ lập trình nằm ở cuối ngăn xếp công nghệ và ảnh hưởng đến mọi thứ được xây dựng trên chúng và 2) giống như bất kỳ phần mềm nào khác, việc thêm các tính năng dễ dàng hơn là loại bỏ chúng. Vì vậy, tôi nghĩ rằng đó là một chất lượng tốt cho một nhà thiết kế ngôn ngữ để chỉ trích những loại yêu cầu này.
Jason Baker

8
"Cấp, Lisp chỉ là năng động" -> và chỉ là bắt buộc!
pyon

6
@Jules, bạn có phiền khi chia sẻ hướng dẫn sử dụng tối ưu hóa cuộc gọi đuôi trong Python không? Một con trỏ đến một số nguồn sẽ hữu ích.
David Lắc

52

Lược đồ không có kiểu dữ liệu đại số hoặc khớp mẫu nhưng chắc chắn đó là ngôn ngữ chức năng. Làm phiền mọi thứ về Python từ góc độ lập trình chức năng:

  1. Lambdas què quặt. Vì Lambdas chỉ có thể chứa một biểu thức và bạn không thể thực hiện mọi thứ dễ dàng trong ngữ cảnh biểu thức, điều này có nghĩa là các chức năng bạn có thể xác định "đang hoạt động" bị hạn chế.

  2. Ifs là câu lệnh, không phải biểu thức. Điều này có nghĩa, trong số những thứ khác, bạn không thể có lambda với Nếu bên trong nó. (Điều này được sửa bởi các ternaries trong Python 2.5, nhưng nó trông xấu.)

  3. Guido đe dọa sẽ xóa bản đồ, bộ lọc và giảm từng lần một

Mặt khác, trăn có các đóng cửa từ vựng, Lambdas và liệt kê các hiểu biết (thực sự là một khái niệm "chức năng" cho dù Guido có thừa nhận hay không). Tôi thực hiện nhiều chương trình "kiểu chức năng" trong Python, nhưng tôi hầu như không nói nó lý tưởng.


3
Bản đồ, bộ lọc và giảm thực sự không cần thiết trong python. Tôi vẫn chưa thấy một đoạn mã nào được đơn giản hóa bằng cách sử dụng chúng. Ngoài ra, các hàm gọi trong Python có thể rất tốn kém, vì vậy, tốt hơn hết là chỉ nên sử dụng một sự hiểu biết về danh sách / trình tạo hoặc một vòng lặp for.
Jason Baker

2
Đây chính xác là những gì Nathan Sanders nói dưới đây: "Python không thúc đẩy lập trình chức năng mặc dù nó hoạt động khá tốt." Nếu Guido muốn Python trở thành một ngôn ngữ chức năng, anh ta sẽ khiến việc triển khai hoạt động đủ tốt để sử dụng các hàm bỏ đi và sẽ mô tả Lambdas đến mức bạn thực sự có thể sử dụng bản đồ / bộ lọc / giảm theo những cách hữu ích. Mặt khác, những người có chức năng đang bắt đầu thức dậy với sự tuyệt vời của việc hiểu danh sách. Hy vọng chúng ta sẽ không phải chọn cái này hay cái kia.
Jacob B

7
bản đồ và bộ lọc được thay thế một cách tầm thường bằng cách hiểu danh sách. giảm - hầu như luôn luôn - nó không hiệu quả đến mức cần được thay thế bằng chức năng tạo.
S.Lott

13
@ S.Lott làm thế nào để bạn thay thế giảm bằng một máy phát điện?
Antimon

17
@JacobB Danh sách hiểu được có sẵn trong các ngôn ngữ chức năng khoảng 15 năm trước khi Python được phát minh và 25 năm trước khi Python có được triển khai tính năng này. Ý tưởng rằng Python đã ảnh hưởng đến sự lan truyền của họ, hoặc fp đã học được điều này từ Python, hoặc thậm chí đơn giản là sự phổ biến của nó trong thế giới fp sau ngày triển khai Python, chỉ đơn giản là sai. Việc triển khai Python được lấy trực tiếp từ Haskell. Có thể tôi đã hiểu lầm bạn và đó không phải là ý bạn, nhưng tôi cảm thấy bối rối bởi "những người có chức năng đang bắt đầu thức dậy với sự tuyệt vời của việc hiểu danh sách".
itbruce

23

Tôi sẽ không bao giờ gọi hàm Python Python, nhưng bất cứ khi nào tôi lập trình bằng Python, mã này luôn luôn có chức năng gần như hoàn toàn.

Phải thừa nhận rằng, điều đó chủ yếu là do sự hiểu biết danh sách cực kỳ tốt đẹp. Vì vậy, tôi không nhất thiết phải đề xuất Python là ngôn ngữ lập trình chức năng nhưng tôi sẽ đề xuất lập trình chức năng cho bất kỳ ai sử dụng Python.


17

Hãy để tôi chứng minh bằng một đoạn mã được lấy từ câu trả lời cho câu hỏi Python "chức năng" trên SO

Con trăn

def grandKids(generation, kidsFunc, val):
  layer = [val]
  for i in xrange(generation):
    layer = itertools.chain.from_iterable(itertools.imap(kidsFunc, layer))
  return layer

Haskell:

grandKids generation kidsFunc val =
  iterate (concatMap kidsFunc) [val] !! generation

Sự khác biệt chính ở đây là thư viện chuẩn của Haskell có chức năng hữu ích cho lập trình chức năng: trong trường hợp này iterate, concat(!!)


7
Đây là một thay thế một dòng cho grandKids()cơ thể với các biểu thức trình tạo : return reduce(lambda a, v: concat((x for x in kidsFunc(v)) for v in a), xrange(generation), [val]).
Lloeki

6
Và đây là một thứ không cần thiết concat:return reduce(lambda a, v: (x for v in a for x in kidsFunc(v)), xrange(generation), [val])
orgeki

9
@Lloeki: iterate sẽ đơn giản hóa mã đó một cách đáng kể và (x for v in a for x in childrenFunc (v)) rõ ràng hơn nhiều so với concatMap (KidsFunc). Python thiếu các tích hợp bậc cao đẹp hơn làm cho mã tương đương khó hiểu và dài dòng so với Haskell.
Phob

2
concat có thể được thay thế bằngitertools.chain.from_iterable
Antimon

@Antimony: tốt để biết. thx
yairchu

14

Một điều thực sự quan trọng cho câu hỏi này (và câu trả lời) là như sau: Cái quái gì là lập trình chức năng, và các thuộc tính quan trọng nhất của nó là gì. Tôi sẽ cố gắng đưa ra quan điểm của tôi về nó:

Lập trình hàm rất giống như viết toán trên bảng trắng. Khi bạn viết phương trình trên bảng trắng, bạn không nghĩ về thứ tự thực hiện. Không có (thường) không có đột biến. Bạn không quay lại vào ngày hôm sau và xem xét nó, và khi bạn thực hiện lại các phép tính, bạn sẽ nhận được một kết quả khác (hoặc bạn có thể, nếu bạn đã uống cà phê tươi :)). Về cơ bản, những gì trên bảng là ở đó, và câu trả lời đã có sẵn khi bạn bắt đầu viết mọi thứ xuống, bạn chỉ không nhận ra nó là gì.

Lập trình chức năng là rất nhiều như thế; bạn không thay đổi mọi thứ, bạn chỉ cần đánh giá phương trình (hoặc trong trường hợp này là "chương trình") và tìm ra câu trả lời là gì. Chương trình vẫn còn đó, chưa sửa đổi. Tương tự với dữ liệu.

Tôi sẽ xếp hạng các tính năng sau đây là các tính năng quan trọng nhất của lập trình chức năng: a) tính minh bạch tham chiếu - nếu bạn đánh giá cùng một tuyên bố tại một thời điểm và địa điểm khác, nhưng với cùng các giá trị biến, nó vẫn có nghĩa như nhau. b) không có tác dụng phụ - cho dù bạn nhìn chằm chằm vào bảng trắng bao lâu, phương trình mà một người khác đang nhìn vào bảng trắng khác sẽ không vô tình thay đổi. c) hàm cũng là giá trị. có thể được truyền xung quanh và áp dụng với, hoặc các biến khác. d) thành phần hàm, bạn có thể thực hiện h = g · f và do đó xác định hàm mới h (..) tương đương với cách gọi g (f (..)).

Danh sách này theo thứ tự ưu tiên của tôi, vì vậy tính minh bạch tham chiếu là quan trọng nhất, tiếp theo là không có tác dụng phụ.

Bây giờ, nếu bạn đi qua python và kiểm tra ngôn ngữ và thư viện hỗ trợ tốt như thế nào, và đảm bảo, những khía cạnh này - thì bạn đang trên đường trả lời câu hỏi của chính mình.


2
Các hàm là hạng nhất trong Python.
Carl Smith

@CarlSmith Cái đó, nhưng để lại 3/4 mà Python không có. : - \
arya

1
Tôi không nghĩ Python là một ngôn ngữ tốt cho lập trình chức năng. Bây giờ tôi thậm chí không chắc ý của tôi về ý kiến ​​đó là trung thực. Nó dường như không liên quan đến câu trả lời. Tôi sẽ xóa nó, nhưng sau đó bình luận của bạn sẽ ra khỏi bối cảnh.
Carl Smith

1
Tính minh bạch tham chiếu và tính bất biến không thực sự là các tính năng ngôn ngữ. Có, một số ngôn ngữ (Haskell) nhấn mạnh chúng và làm cho khó có thể không có chúng, nhưng bạn có thể tạo một hàm trong suốt tham chiếu hoặc một đối tượng bất biến trong bất kỳ ngôn ngữ nào. Bạn chỉ cần làm việc xung quanh thư viện tiêu chuẩn, thường sẽ vi phạm chúng.
Kevin

Ngoài ra, Python có hỗ trợ cho cả cà ri và thành phần, nhưng không phải ở cấp độ ngôn ngữ, thay vào đó, nó nằm trong thư viện chuẩn.
Kevin

10

Python gần như là một ngôn ngữ chức năng. Đó là "lite chức năng".

Nó có các tính năng bổ sung, vì vậy nó không đủ tinh khiết cho một số người.

Nó cũng thiếu một số tính năng, vì vậy nó không đủ cho một số tính năng.

Các tính năng còn thiếu tương đối dễ viết. Kiểm tra các bài viết như thế này trên FP trong Python.


2
Đối với hầu hết các phần, tôi đồng ý với bài viết này. Nhưng tôi không thể đồng ý với việc Python là ngôn ngữ chức năng. Nó rất khuyến khích lập trình bắt buộc, và thật khó để không có "tính năng bổ sung" mà bạn đề cập. Tôi nghĩ tốt nhất nên coi Python là "lite chức năng" như bạn đã làm trong bài viết khác. :-)
Jason Baker

8
-1 Xin lỗi, không. Ý tôi là, chỉ là không. Các ngôn ngữ chức năng cung cấp các cấu trúc tạo điều kiện cho lý luận chính thức: quy nạp (ML), phương trình (Haskell). Đóng cửa và các chức năng ẩn danh chỉ là đường cú pháp cho mẫu chiến lược.
pyon

8

Một lý do khác không được đề cập ở trên là nhiều hàm tích hợp và phương thức của các kiểu dựng sẵn sửa đổi một đối tượng nhưng không trả về đối tượng đã sửa đổi. Nếu những đối tượng sửa đổi đó được trả về, điều đó sẽ làm cho mã chức năng sạch hơn và súc tích hơn. Ví dụ: nếu some_list.append (some_object) trả về some_list với some_object được nối thêm.


4

Ngoài các câu trả lời khác, một lý do Python và hầu hết các ngôn ngữ đa mô hình khác không phù hợp với lập trình chức năng thực sự là do trình biên dịch / máy ảo / thời gian chạy của chúng không hỗ trợ tối ưu hóa chức năng. Loại tối ưu hóa này đạt được bởi trình biên dịch hiểu các quy tắc toán học. Ví dụ, nhiều ngôn ngữ lập trình hỗ trợ một mapchức năng hoặc phương thức. Đây là một hàm khá chuẩn lấy một hàm làm một đối số và lặp lại làm đối số thứ hai sau đó áp dụng hàm đó cho từng phần tử trong iterable.

Dù sao thì nó map( foo() , x ) * map( foo(), y )cũng giống như vậy map( foo(), x * y ). Trường hợp sau thực sự nhanh hơn trường hợp trước vì trường hợp trước thực hiện hai bản sao trong đó trường hợp sau thực hiện một bản.

Các ngôn ngữ chức năng tốt hơn nhận ra các mối quan hệ dựa trên toán học này và tự động thực hiện tối ưu hóa. Các ngôn ngữ không dành riêng cho mô hình chức năng có thể sẽ không tối ưu hóa.


map( foo() , x ) * map( foo(), y ) == map( foo(), x * y )không đúng với tất cả các chức năng Ví dụ, xem xét trường hợp khi footính đạo hàm.
Eli Korvigo

1
Tôi nghĩ rằng anh ấy có nghĩa là +thay vì *.
dùng1747134

Bạn đang giả sử foo () là tuyến tính?
juan Isaza

thuộc tính đó KHÔNG đúng với foo (x) = x + 1. Như (x + 1) * (y + 1)! = X * y + 1.
juan Isaza
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.