Chức năng hạng nhất


11

Tôi bắt đầu nghiêm túc xem Lisp vào cuối tuần này (ý tôi là tôi chỉ mới học Lisp và không quay lại các dự án trong C #) và phải nói rằng tôi yêu nó. Tôi đã học được các ngôn ngữ chức năng khác (F #, Haskell, Erlang) nhưng không cảm nhận được sức hút mà Lisp đã mang lại cho tôi.

Bây giờ khi tôi tiếp tục học Lisp, tôi đã bắt đầu tự hỏi tại sao các ngôn ngữ phi chức năng không hỗ trợ các chức năng hạng nhất. Tôi biết rằng các ngôn ngữ như C # có thể làm những điều tương tự với các đại biểu và ở một mức độ nào đó bạn có thể sử dụng các con trỏ tới các chức năng trong C / C ++ nhưng có lý do nào khiến điều này không bao giờ trở thành một tính năng trong các ngôn ngữ đó không? Có một nhược điểm để làm cho chức năng hạng nhất? Đối với tôi, nó cực kỳ hữu ích, vì vậy tôi không biết tại sao nhiều ngôn ngữ (ngoài mô hình chức năng) không thực hiện nó.

[Chỉnh sửa] Tôi đánh giá cao các phản hồi cho đến nay. Vì tôi đã được chứng minh rằng bây giờ nhiều ngôn ngữ hỗ trợ các chức năng hạng nhất, tôi sẽ đặt lại câu hỏi là tại sao phải mất quá nhiều thời gian để các ngôn ngữ thực hiện chúng? [/Biên tập]


3
Tôi không đồng ý rằng C # có thể làm "những điều tương tự". Tôi muốn nói rằng nó có các chức năng hạng nhất cho tất cả ý định và mục đích (loại này làm hỏng tiền đề của bạn). Nó có một cú pháp cho các chữ theo hàm, bạn có thể nhồi các hàm vào các biến, v.v.
Logan Capaldo

@Logan - Tôi đã tranh luận về việc có bao gồm các ví dụ về C # hay không nhưng quyết định dựa trên mục nhập wikipedia này . Theo mục này, không có các hàm hạng nhất thực sự vì "vì các đối tượng giữ trạng thái động của hàm phải được xây dựng thủ công". Tuy nhiên, nếu đó là một tuyên bố không chính xác, tôi rất muốn biết tại sao! (Sau tất cả, tôi ở đây để học và không bị mắc kẹt theo cách của mình!)
Jetti

2
mục wikipedia đó đã lỗi thời. Một C # hiện đại cung cấp một lambda thích hợp.
SK-logic

Tôi nghĩ rằng đoạn văn đó được viết kém. Có vẻ như nói rằng functor C ++ không phải là hạng nhất và cũng không phải là đại biểu của C #? Mặc dù tôi có thể đồng ý với lập luận cho functor, tôi không chắc chắn tôi sẽ làm thế nào cho các đại biểu C #. Nó cũng nói "(xem lambda nâng)", đó là một tuyên bố về các ngôn ngữ này hỗ trợ việc đóng từ vựng thay vì "các chức năng như các giá trị". Bạn có thể có cái này mà không có cái kia. Ngay cả khi đó là trường hợp, nó không đúng với C #, nó không có các đóng cửa từ vựng. Một phần của vấn đề là "chức năng hạng nhất" có thể là một thuật ngữ mơ hồ.
Logan Capaldo

1
@Logan & SK-Logic - Tôi đánh giá cao phản hồi của bạn và luôn mang tính xây dựng. Tôi vẫn đang học và thật tuyệt khi không được rực lửa, vì vậy tôi đánh giá cao điều đó. Cảm ơn rất nhiều cho các ý kiến ​​và câu trả lời!
Jetti

Câu trả lời:


5

C #, VB.NET, Python, JavaScript, thậm chí C ++ 0x cung cấp các hàm hạng nhất.

Cập nhật:

Việc thực hiện đóng cửa đòi hỏi phải nâng lambda - một kỹ thuật ban đầu khá xa lạ đối với các lập trình viên cấp bách. Các hàm hạng nhất thích hợp (bao gồm các bao đóng) yêu cầu ít nhất một số hỗ trợ từ bộ thực thi và VM. Cũng phải mất một thời gian để áp dụng các kỹ thuật chức năng cũ vào thế giới mệnh lệnh.

Và, phần quan trọng nhất - các chức năng hạng nhất hầu như không thể sử dụng được nếu không có bộ sưu tập rác. Nó đã được đưa vào một chương trình đại chúng chỉ gần đây, với Java và do đó, .NET. Và cách tiếp cận Java ban đầu là đơn giản hóa ngôn ngữ để giữ cho nó dễ hiểu bởi các lập trình viên trung bình. .NET trước tiên theo sau và chỉ mới chia tay theo hướng đó (IMHO, khá hợp lý và phù hợp).

Tất cả các khái niệm như vậy cần có thời gian để được tiêu hóa bởi ngành công nghiệp.


Tôi đã chỉnh sửa câu hỏi ban đầu dựa trên các câu trả lời.
Jetti

Các hàm hạng nhất! = Đóng (và GC rất cần thiết hơn cho hàm sau). Mặc dù, vâng, chúng có liên quan chặt chẽ với nhau và hiếm khi bạn thấy chúng tách biệt.

@delnan, không có ít nhất một số dạng hàm đóng từ vựng không thể tổng hợp và không thể xây dựng được trong thời gian chạy, điều này là bắt buộc đối với định nghĩa hàm hạng nhất.
SK-logic

Không. Bạn có thể không cho phép tham chiếu đến các biến của phạm vi kèm theo (hoặc sao chép các giá trị của biến được tham chiếu tại thời điểm tạo hàm - dunno nếu điều này được tính là đóng). Kết quả không đặc biệt hữu ích, nhưng các chức năng vẫn có thể được xây dựng trong thời gian chạy mà không bị đóng và do đó bạn có các hàm hạng nhất (kỳ lạ).

1
@ SK-logic: C ++ 0x cung cấp các bao đóng không có bộ sưu tập rác với thủ thuật thông thường -> nếu biến vượt khỏi phạm vi và bạn vẫn bám vào một tham chiếu, sử dụng tham chiếu là hành vi không xác định. Vì vậy, nó có thể ... nó chỉ đặt honus cho nhà phát triển.
Matthieu M.

5

Các chức năng hạng nhất là rất ít thú vị mà không cần đóng cửa.

Việc đóng cửa không thực sự khả thi nếu không có một số loại quản lý động phạm vi (thu gom rác). Một trong những điều làm cho C / C ++ có hiệu suất cao là thực tế là việc biên dịch một ngôn ngữ mà bạn quản lý bộ nhớ theo cách thủ công sẽ dễ dàng hơn rất nhiều, do đó loại C / C ++ ra khỏi phương trình.

Và sau đó, có, có vấn đề về tác động OOP. Bạn không còn có một sự tách biệt của các phương thức và thuộc tính. Các phương thức là các thuộc tính chấp nhận các hàm như các giá trị. Trong bối cảnh đó, điều gì thực sự có nghĩa là quá tải các phương thức vì bạn có thể trao đổi những thứ ngớ ngẩn bất cứ lúc nào. Bạn có thể thực sự thêm điều đó vào Java, chẳng hạn, mà không đưa ra một sự thay đổi mô hình chính cho toàn bộ ngôn ngữ? Hợp đồng hoặc ý nghĩa của bảo mật (IMO sai) mà mọi người nhận được từ việc biết cấu trúc đảm bảo nó sẽ luôn hoạt động theo cùng một cách mỗi lần? Có thể có ý nghĩa khi coi các hàm gắn với các đối tượng là các phương thức như một sinh vật khác trong các ngôn ngữ cho phép các hàm tồn tại độc lập ở nơi đầu tiên nhưng trong Java đó không thực sự là một lựa chọn.

Trong JavaScript, OOP và chức năng rất đan xen và cá nhân tôi sẽ thấy khó có thể xem các chức năng hạng nhất như bất cứ thứ gì ngoại trừ trong các ngôn ngữ mà chúng không có. Nhưng điều đó đòi hỏi một số thay đổi mô hình khác bao gồm mức độ biến đổi cao trong các đối tượng và phương thức của chúng cũng như có thể gọi các hàm như thể chúng là thành viên của bất kỳ đối tượng nào đang di chuyển.

Các ngôn ngữ không chỉ là bộ công cụ đầy những con hickey để hoàn thành phần lớn công việc. Chúng là những mô thức. Gói ý tưởng, chiến lược và ý kiến ​​rằng tất cả các loại đi cùng nhau theo cách kết nối (hoặc dường như được kết nối). Trong rất nhiều trường hợp, các chức năng như danh từ và động từ thực sự không phù hợp với mô hình nào cả hoặc tốt nhất là một sự phù hợp không hoàn hảo.

Điều đó nói rằng, tôi cảm thấy như mình đang thiếu một cánh tay khi bị buộc phải viết mã mà không có chúng.


Bạn có thể coi tất cả các phương pháp là niêm phong hoặc chỉ niêm phong những phương pháp bạn quan tâm nhất, và bạn sẽ ổn thôi.
Alexei Averchenko

@AlexeiAverchenko Tôi rất vui khi nói rằng tôi không chắc chắn 100% ý của bạn là gì. Xin lỗi tôi đã bỏ lỡ bình luận của bạn cho đến bây giờ. Googling "phương pháp niêm phong."
Erik Reppen

4

Nó không thực sự là không thể. Nhưng đó là một tính năng khác, và ngân sách phức tạp bị hạn chế. Nó có thể được coi là không cần thiết bởi (hoặc thậm chí không xảy ra) nhà thiết kế ngôn ngữ - "tại sao chúng ta cần phải vượt qua các chức năng xung quanh? Ngôn ngữ này là để lập trình HĐH!". Nó cũng có thể mất nỗ lực đáng kể để hợp nhất với phần còn lại của ngôn ngữ. Chẳng hạn, một loại hàm có thể yêu cầu bổ sung nghiêm túc cho hệ thống loại (ví dụ: trong Java), thậm chí nhiều hơn nếu bạn có quá tải (cảm ơn @Renesis đã chỉ ra điều đó). Bạn cũng cần cung cấp một số mã thư viện để sử dụng khả thi - không ai muốn maptự xác định .

Nó cũng có thể làm cho các mục tiêu khác của ngôn ngữ khó đạt được hơn:

  • Nó trở nên "khó hơn" để tối ưu hóa (không thực sự khó hơn trong nhiều trường hợp, nhưng đòi hỏi các cách tiếp cận khác với cách tiếp cận bạn đã từng sử dụng). Ví dụ, nếu các chức năng toàn cầu có thể được chỉ định lại, bạn phải chứng minh rằng fkhông bao giờ được gán lại trước khi bạn đặt nội tuyến.
  • Theo cách tương tự, nếu bạn tạo các hàm đối tượng đầy đủ tính năng, bạn cần một trình biên dịch thông minh và JIT để loại bỏ chi phí liên quan đến điều đó và thực hiện cuộc gọi hiệu quả (tốc độ không gian) như đẩy các đối số và trả về địa chỉ và nhảy tới một số địa chỉ.
  • Như đã đề cập, đó là một tính năng đầy đủ khác và các bước đầu tiên để hỗ trợ một mô hình khác - nếu bạn muốn một ngôn ngữ đơn giản, có lẽ bạn không đủ khả năng để thêm các chức năng hạng nhất mà không cần ném các thứ khác (mà bạn có thể coi là quan trọng hơn nhiều) ngoài.
  • Có lẽ nó chỉ đơn giản là không kết hợp tốt với phần còn lại của ngôn ngữ. Nếu bạn thiết kế ngôn ngữ để trở thành hiện thân tốt nhất của OOP kể từ Smalltalk, bạn có thể muốn tập trung vào đó thay vì đưa ra một mô hình khác, rất khác. Đặc biệt là nếu khó tích hợp cả hai (xem ở trên). Các mô hình N được thực hiện tốt sẽ dễ chịu hơn khi lập trình trong các mô hình N + 1 được thực hiện kém.

Trong cùng một hướng, người ta có thể hỏi tại sao bất kỳ ngôn ngữ lập trình L1 nào không có tính năng F của ngôn ngữ L2 rất khác nhau.


1

Tôi nghĩ vấn đề đầu tiên là một sự hiểu lầm rằng Hướng đối tượng và Chức năng không thể trộn lẫn. Một danh sách nhanh các ngôn ngữ được mô tả, ít nhất là bởi Wikipedia, như cả hai: JavaScript , ActionScript , Python , C # , F # .

Vấn đề thứ hai dường như là một định nghĩa về các hàm hạng nhất - tại sao bạn không xem xét C # để có chúng, chẳng hạn?

Tôi không phải là một chuyên gia ngôn ngữ chức năng, vì vậy xin vui lòng sửa cho tôi trong các nhận xét nếu tôi sai, nhưng tôi hiểu rằng việc bao gồm các chức năng hạng nhất tự nó ít nhiều xác định liệu một ngôn ngữ có hỗ trợ mô hình chức năng hay không .

Câu hỏi thực sự là gì?

Vì vậy, sự hiểu biết rằng ngôn ngữ hướng đối tượng có thể cũng có chức năng, và các thứ tiếng chứa chức năng hạng nhất, có vẻ như những câu hỏi mà bạn đang tìm kiếm được tại sao một số ngôn ngữ hướng đối tượng không chứa chức năng hạng nhất?

Chức năng quá tải

Một trong những lý do chính cho điều này là khó khăn trong việc xác định các hàm hạng nhất khi ngôn ngữ cũng được chọn để hỗ trợ nạp chồng hàm (ví dụ trong Java):

public int dispatchEvent(Event event);
public int dispatchEvent(String event, Object data);

dispatchEventHàm nào sẽ là một biến tham chiếu?

Lập trình dựa trên lớp

Lập trình dựa trên lớp không ngăn các ngôn ngữ hỗ trợ các đối tượng hạng nhất, nhưng nó có thể làm cho nó trở nên khó hiểu hơn hoặc thêm câu hỏi để được trả lời.

ActionScript, ví dụ, như một ngôn ngữ ECMAScript bắt đầu trong một mô hình nhiều chức năng hơn như JavaScript. Các hàm vốn không bị ràng buộc với các đối tượng, về cơ bản chúng có thể được gọi với bất kỳ đối tượng nào theo phạm vi tại thời điểm thực thi.

Khi bạn thêm các lớp (không động), bạn có các hàm vốn đã bị ràng buộc với một thể hiện, không chỉ riêng lớp đó. Điều này ném một vài nếp nhăn vào đặc điểm kỹ thuật Function.applymà tôi thực sự không đào sâu để tìm hiểu xem họ đã xử lý nó như thế nào.


3
"Việc bao gồm các chức năng hạng nhất tự nó ít nhiều làm cho một chức năng ngôn ngữ." - Sai lầm. Đó là một điều kiện tiên quyết, vâng. Nhưng có nhiều hơn thế, như (chỉ gọi tên ba) tránh trạng thái có thể thay đổi, tập trung vào các biểu thức và ứng dụng chức năng thay vì các câu lệnh liên tiếp và nhấn mạnh vào tính minh bạch tham chiếu.

@delnan Bạn nên đi đúng Wikipedia sau đó ... Trừ khi "ngôn ngữ chức năng" và "ngôn ngữ hỗ trợ mô hình chức năng" là hai điều khác nhau.
Nicole

1
@Rensis: Chúng là những thứ khác nhau. Bạn có thể tạo một cái gì đó giống như các đối tượng (thậm chí bao gồm cả vtables) trong C, nhưng nó không đẹp. Đi từ "có chức năng hạng nhất" sang "là một ngôn ngữ chức năng" không hoàn toàn xấu, nhưng nó vẫn là một sự khác biệt. Ngoài ra, wiki (chính xác) nêu trên trang bạn liên kết đến "Các tính năng này là cần thiết cho phong cách lập trình chức năng [...]." (tức là: không phải "tính năng xác định") và liệt kê một số tính năng khác trên trang trên FP.

@delnan, Được rồi, tôi đã sửa từ ngữ của tôi. Tôi chỉ có nghĩa là điều kiện để một ngôn ngữ được coi là hỗ trợ một mô hình chức năng - cũng như trang Wikipedia cho mỗi ngôn ngữ mà tôi đã liệt kê. Tôi không có ý nói rằng đó là một phần của phong cách lập trình chức năng .
Nicole

0

Tôi đã tự hỏi điều tương tự bản thân mình. Khi tôi ở trong một ngôn ngữ với lambdas, tôi sử dụng chúng rất nhiều. Ngay cả PHP cũng trở nên tốt hơn khi bạn nhận ra rằng bạn có thể đóng cửa với php 5.3 (nó không đẹp như lisp, nhưng vẫn tốt hơn)

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.