Có phải lập trình chức năng làm tăng 'khoảng cách đại diện' giữa các vấn đề và giải pháp? [đóng cửa]


18

Vì ngôn ngữ máy (ví dụ 0110101000110101:) ngôn ngữ máy tính thường phát triển thành các hình thức trừu tượng cao hơn, nói chung giúp dễ hiểu mã hơn khi áp dụng cho một vấn đề. Trình biên dịch là một bản tóm tắt về mã máy, C là phần trừu tượng so với trình biên dịch, v.v.

Thiết kế hướng đối tượng dường như rất tốt trong việc cho phép chúng ta mô hình hóa một vấn đề về các đối tượng, ví dụ, vấn đề của hệ thống đăng ký khóa học đại học có thể được mô hình hóa với một Courselớp, một Studentlớp, v.v. Sau đó, khi chúng ta viết giải pháp trong ngôn ngữ OO, chúng tôi có các lớp tương tự nhận trách nhiệm và thường hữu ích cho thiết kế, đặc biệt là để mô đun hóa mã. Nếu tôi đưa ra vấn đề này cho 10 nhóm độc lập giải quyết nó bằng phương pháp OO, thông thường, 10 giải pháp sẽ có các lớp liên quan đến vấn đề chung. Có thể có nhiều sự khác biệt khi bạn bắt đầu tham gia vào khớp nối và tương tác của các lớp đó, vì vậy không có thứ gọi là "khoảng cách biểu diễn bằng không".

Trải nghiệm của tôi với Lập trình chức năng rất hạn chế (không sử dụng trong thế giới thực, chỉ có các chương trình loại Hello World). Tôi không thấy các ngôn ngữ như vậy cho phép dễ dàng ánh xạ các giải pháp FP đến các vấn đề (với khoảng cách biểu diễn thấp) theo cách mà các ngôn ngữ OO làm.

Tôi hiểu những lợi thế của FP liên quan đến lập trình đồng thời. Nhưng tôi có thiếu một cái gì đó không, hay là FP không phải là về việc thu hẹp khoảng cách đại diện (làm cho các giải pháp dễ hiểu hơn)?

Một cách khác để hỏi điều này: liệu mã FP của 10 đội khác nhau giải quyết cùng một vấn đề trong thế giới thực có nhiều điểm chung không?


Từ Wikipedia về Trừu tượng (khoa học máy tính) (nhấn mạnh của tôi):

Các ngôn ngữ lập trình hàm thường thể hiện sự trừu tượng liên quan đến các hàm , chẳng hạn như trừu tượng lambda (biến một thuật ngữ thành hàm của một số biến), các hàm bậc cao hơn (tham số là hàm), trừu tượng khung (biến thuật ngữ thành hàm của biến).

Khoảng cách về đại diện có thể có khả năng gia tăng, bởi vì [một số] các vấn đề trong thế giới thực không được mô hình hóa dễ dàng với sự trừu tượng như vậy.


Một cách khác tôi thấy giảm khoảng cách biểu diễn là trong việc truy tìm các yếu tố giải pháp trở lại vấn đề. Các 0's và 1s trong mã máy là rất khó theo dõi trở lại, trong khi các Studentlớp học rất dễ dàng để dấu vết lại. Không phải tất cả các lớp OO dễ dàng theo dõi không gian vấn đề, nhưng nhiều người làm.

Không trừu tượng FP luôn cần giải thích để tìm ra phần nào của không gian vấn đề mà họ đang giải quyết (ngoài các vấn đề toán học )?OK - Tôi tốt về phần này. Sau khi xem xét nhiều ví dụ khác, tôi thấy cách trừu tượng của FP rất rõ ràng đối với các phần của vấn đề được thể hiện trong xử lý dữ liệu.


Câu trả lời được chấp nhận cho một câu hỏi liên quan Có thể sử dụng UML để mô hình hóa chương trình Chức năng không? - cho biết "Các lập trình viên chức năng không có nhiều sử dụng cho sơ đồ." Tôi thực sự không quan tâm nếu đó là UML, nhưng nó khiến tôi băn khoăn về việc tóm tắt FP dễ hiểu / giao tiếp, nếu không có sơ đồ nào được sử dụng rộng rãi (giả sử câu trả lời này là chính xác). Một lần nữa, mức độ sử dụng / hiểu biết về FP của tôi là không đáng kể, vì vậy tôi hiểu rằng không cần sơ đồ trên các chương trình FP đơn giản.

Thiết kế OO có mức độ trừu tượng của chức năng / lớp / gói, với sự đóng gói (kiểm soát truy cập, ẩn thông tin) ở mỗi cấp độ, giúp cho việc quản lý phức tạp dễ dàng hơn. Đây là những yếu tố cho phép đi từ vấn đề đến giải pháp và trở lại dễ dàng hơn.


Nhiều câu trả lời nói về cách phân tích và thiết kế được thực hiện trong FP theo cách tương tự như OO, nhưng không ai trích dẫn bất cứ điều gì ở cấp độ cao cho đến nay (paul đã trích dẫn một số nội dung thú vị, nhưng ở mức độ thấp). Tôi đã làm rất nhiều Googling ngày hôm qua và tìm thấy một số cuộc thảo luận thú vị. Sau đây là từ Tái cấu trúc các chương trình chức năng của Simon Thompson (2004) (nhấn mạnh của tôi)

Trong việc thiết kế một hệ thống hướng đối tượng, phải thừa nhận rằng thiết kế sẽ đi trước lập trình. Các thiết kế sẽ được viết bằng một hệ thống như UML, được hỗ trợ trong các công cụ như Eclipse. Các lập trình viên mới bắt đầu cũng có thể học một phương pháp thiết kế trực quan bằng cách sử dụng các hệ thống như BlueJ. Làm việc trên một phương pháp tương tự để lập trình chức năng được báo cáo trong FAD: Phân tích và thiết kế chức năng , nhưng có rất ít công việc khác tồn tại. Có thể có một số lý do cho việc này.

  • Các chương trình chức năng hiện có có quy mô không yêu cầu thiết kế. Nhiều chương trình chức năng là nhỏ, nhưng những chương trình khác, chẳng hạn như Trình biên dịch Haskell của Glasgow, là đáng kể.

  • Các chương trình chức năng trực tiếp mô hình hóa miền ứng dụng, do đó kết xuất thiết kế không liên quan. Trong khi các ngôn ngữ chức năng cung cấp nhiều loại trừu tượng mạnh mẽ, thật khó để tranh luận rằng những ngôn ngữ này cung cấp tất cả và chỉ những trừu tượng cần thiết để mô hình hóa thế giới thực.

  • Các chương trình chức năng được xây dựng như một loạt các nguyên mẫu đang phát triển.

Trong luận án tiến sĩ được trích dẫn ở trên , lợi ích của việc sử dụng phương pháp phân tích và thiết kế (ADM) được phác thảo độc lập với mô hình. Nhưng một lập luận được đưa ra là các ADM phải phù hợp với mô hình triển khai. Đó là, OOADM hoạt động tốt nhất cho lập trình OO và không được áp dụng tốt cho một mô hình khác như FP. Đây là một trích dẫn tuyệt vời mà tôi nghĩ rằng diễn giải những gì tôi gọi là khoảng cách đại diện:

người ta có thể tranh luận về việc mô hình nào cung cấp sự hỗ trợ tốt nhất cho phát triển phần mềm, nhưng người ta đạt được gói phát triển tự nhiên, hiệu quả và hiệu quả nhất khi một mô hình duy nhất từ ​​mô tả vấn đề cho đến thực hiện và phân phối.

Dưới đây là bộ sơ đồ được đề xuất bởi FAD:

  • sơ đồ phụ thuộc chức năng trình bày một chức năng với những chức năng mà nó sử dụng trong quá trình thực hiện;
  • biểu đồ phụ thuộc loại cung cấp cùng một dịch vụ cho các loại; và,
  • sơ đồ phụ thuộc mô-đun trình bày quan điểm của kiến ​​trúc mô-đun của hệ thống.

Có một nghiên cứu trường hợp trong phần 5.1 của luận án FAD, đây là một hệ thống để tự động hóa việc sản xuất dữ liệu liên quan đến một giải đấu bóng đá (bóng đá). Các yêu cầu là 100% chức năng, ví dụ: kết quả bóng đá đầu vào, tạo bảng giải đấu, bảng ghi bàn, bảng tham dự, chuyển cầu thủ giữa các đội, cập nhật dữ liệu sau kết quả mới, v.v. Không đề cập đến cách FAD hoạt động để giải quyết các yêu cầu phi chức năng được ghi lại , ngoài việc nói rằng "chức năng mới nên được cho phép với chi phí tối thiểu", một điều gần như không thể kiểm tra.

Đáng buồn thay, ngoài FAD, tôi không thấy bất kỳ tài liệu tham khảo hiện đại nào cho các ngôn ngữ mô hình hóa (trực quan) được đề xuất cho FP. UML là một mô hình khác, vì vậy chúng ta nên quên điều đó.


1
Bình luận không dành cho thảo luận mở rộng; cuộc trò chuyện này đã được chuyển sang trò chuyện .
maple_shaft

Câu trả lời:


20

Các dữ liệu cơ bản được cấu trúc giống nhau trong hầu hết các mô hình. Bạn sẽ có một Student, a Course, v.v. cho dù đó là một đối tượng, một cấu trúc, một bản ghi hoặc bất cứ điều gì. Sự khác biệt với OOP không phải là cách cấu trúc dữ liệu, mà là cách các chức năng được cấu trúc.

Tôi thực sự tìm thấy các chương trình chức năng phù hợp hơn nhiều với cách tôi nghĩ về một vấn đề. Ví dụ, để lên kế hoạch cho lịch học của học sinh cho học kỳ tiếp theo, bạn nghĩ về những thứ như danh sách các khóa học mà học sinh đã hoàn thành, các khóa học trong chương trình cấp bằng của học sinh, các khóa học được cung cấp trong học kỳ này, các khóa học mà học sinh đã hoàn thành các điều kiện tiên quyết, các khóa học có thời gian không xung đột, v.v.

Đột nhiên, không rõ lớp nào sẽ tạo và lưu trữ tất cả các danh sách này. Thậm chí ít hơn khi bạn có sự kết hợp phức tạp của các danh sách này. Tuy nhiên, bạn phải chọn một lớp.

Trong FP, bạn viết các hàm có một sinh viên và một danh sách các khóa học và trả về một danh sách các khóa học được lọc. Bạn có thể nhóm tất cả các chức năng như vậy với nhau trong một mô-đun. Bạn không cần phải kết hợp nó với lớp này hay lớp khác.

Vì vậy, các mô hình dữ liệu của bạn cuối cùng trông giống như cách các lập trình viên OOP nghĩ về các mô hình của họ, trước khi chúng bị ô nhiễm với các lớp không có mục đích nào khác ngoài việc cung cấp các vị trí thuận tiện để đặt các hàm hoạt động trên các kết hợp của các lớp khác. Không có CourseStudentFilterListlớp lạ hoặc tương tự mà bạn luôn luôn cần trong OOP, nhưng không bao giờ nghĩ về thiết kế ban đầu.


5
@BenAaronson vấn đề theo kinh nghiệm của tôi là sau đó bất kỳ chức năng nào liên quan đến học sinh sẽ được thêm vào lớp Sinh viên và trách nhiệm của nó tăng lên cho đến khi bạn cần giới thiệu StudentCourseFiltererđể giữ mọi thứ có thể quản lý được
AlexFoxGill

3
Phương pháp @Den Hybrid có giá trị của họ. Tuy nhiên, điều đó không đúng khi phân biệt là giả tạo. Có rất nhiều sự thỏa hiệp mà Scala phải thực hiện để phù hợp với OOP và FP (suy luận kiểu kém mạnh mẽ hơn là một. Sự phức tạp là một vấn đề khác: vâng, một ngôn ngữ pha trộn OOP và FP có xu hướng phức tạp hơn - như "khó sử dụng và hiểu "- hơn một trong những anh em thuần túy hơn ở hai thái cực).
Andres F.

3
@Den Xem thêm Lập trình "Chủ yếu là chức năng" của Erik Meijer không hoạt động " , lập luận chống lại cách tiếp cận lai và để đi đầy đủ" chủ nghĩa cơ bản "FP.
Andres F.

4
@Den Sự phổ biến mà bạn mô tả là, tôi sợ, một tai nạn của lịch sử. Tôi sử dụng Scala tại nơi làm việc và đó là một con quái vật phức tạp do cách nó cố gắng trộn lẫn FP và OOP. Chuyển sang một ngôn ngữ FP thực sự như Haskell cảm thấy như một luồng không khí trong lành - và tôi cũng không nói từ góc độ học thuật!
Andres F.

3
@ Tôi không thể nói một trong hai chức năng đó làm gì. Tuy nhiên, tôi có thể cho bạn biết nếu hàm đầu tiên trong FP là sắp xếp hoặc bộ lọc, nó sẽ được đặt tên là bộ lọc hoặc sắp xếp , chứ không phải func(giống như bạn đặt tên cho nó IFilter, v.v.). Nói chung, trong FP bạn đặt tên cho nó funchoặc fkhi nào sorthoặc filterlà một lựa chọn hợp lệ! Ví dụ, khi viết một hàm bậc cao hơn lấy functham số.
Andres F.

10

Khi tôi học lớp Java của tôi nhiều năm trước, chúng tôi dự kiến ​​sẽ trình bày các giải pháp của mình cho toàn bộ lớp, vì vậy tôi đã thấy mọi người nghĩ như thế nào; Làm thế nào họ giải quyết vấn đề một cách hợp lý. Tôi hoàn toàn mong đợi các giải pháp để tập hợp xung quanh ba hoặc bốn giải pháp phổ biến. Thay vào đó, tôi theo dõi khi 30 sinh viên giải quyết vấn đề theo 30 cách hoàn toàn khác nhau.

Đương nhiên, khi các lập trình viên non trẻ có được kinh nghiệm, họ sẽ tiếp xúc với các mẫu phần mềm phổ biến, bắt đầu sử dụng các mẫu đó trên mã của họ và sau đó các giải pháp của họ có thể kết hợp xung quanh một vài chiến lược tối ưu. Các mẫu này tạo thành một ngôn ngữ kỹ thuật mà theo đó các nhà phát triển có kinh nghiệm có thể giao tiếp.

Ngôn ngữ kỹ thuật làm nền tảng cho lập trình chức năng là toán học . Theo đó, các vấn đề phù hợp nhất để giải quyết bằng lập trình hàm thực chất là các vấn đề toán học. Theo toán học, tôi không đề cập đến loại toán mà bạn sẽ thấy trong các giải pháp kinh doanh, như phép cộng và phép trừ. Thay vào đó, tôi đang nói về loại toán mà bạn có thể thấy trên Math Overflow, hoặc loại toán bạn sẽ thấy trong công cụ tìm kiếm của Orbitz (nó được viết bằng Lisp).

Định hướng này có một số phân nhánh quan trọng cho các lập trình viên chức năng đang cố gắng giải quyết các vấn đề lập trình trong thế giới thực:

  1. Lập trình chức năng là khai báo nhiều hơn bắt buộc; Nó quan tâm chủ yếu đến việc nói cho máy tính biết phải làm gì chứ không phải làm thế nào.

  2. Các chương trình hướng đối tượng thường được xây dựng từ trên xuống. Một thiết kế lớp được tạo và các chi tiết được điền vào. Các chương trình chức năng thường được xây dựng từ dưới lên, bắt đầu với các hàm nhỏ, chi tiết được kết hợp thành các hàm cấp cao hơn.

  3. Lập trình chức năng có thể có lợi thế cho việc tạo mẫu logic phức tạp, xây dựng các chương trình linh hoạt có thể thay đổi và phát triển hữu cơ và xây dựng phần mềm nơi thiết kế ban đầu không rõ ràng.

  4. Các chương trình hướng đối tượng có thể phù hợp hơn cho các lĩnh vực kinh doanh vì các lớp, thông điệp giữa các đối tượng và các mẫu phần mềm đều cung cấp một cấu trúc ánh xạ tới miền doanh nghiệp, nắm bắt thông tin kinh doanh của nó và ghi lại tài liệu đó.

  5. Bởi vì tất cả các phần mềm thực tế tạo ra các hiệu ứng phụ (I / O), các ngôn ngữ lập trình chức năng thuần túy đòi hỏi phải có một cơ chế để tạo ra các hiệu ứng phụ đó trong khi vẫn còn thuần túy về mặt toán học (các đơn nguyên).

  6. Các chương trình chức năng có thể được chứng minh dễ dàng hơn, do tính chất toán học của chúng. Hệ thống loại của Haskell có thể tìm thấy mọi thứ vào thời gian biên dịch mà hầu hết các ngôn ngữ OO không thể.

Và như thế. Cũng như nhiều thứ trong điện toán, các ngôn ngữ hướng đối tượng và ngôn ngữ chức năng có sự đánh đổi khác nhau.

Một số ngôn ngữ OO hiện đại đã áp dụng một số khái niệm lập trình chức năng hữu ích, để bạn có thể có được cả hai thế giới tốt nhất. Linq, và các tính năng ngôn ngữ đã được thêm vào để hỗ trợ nó, là một ví dụ tốt về điều đó.


6
@thepacker: Bạn có trạng thái trong lập trình chức năng. Chỉ có cách biểu diễn là khác nhau: trong OOP bạn sử dụng các biến có thể thay đổi trong khi trong lập trình chức năng, bạn có thể sử dụng các luồng trạng thái vô hạn được tạo khi đang di chuyển khi chương trình cần kiểm tra chúng. Thật không may, trạng thái thường được xác định với các biến có thể thay đổi, như thể các biến có thể thay đổi là cách duy nhất để biểu diễn trạng thái. Kết quả là, nhiều người tin rằng một ngôn ngữ lập trình không có biến đổi có thể mô hình hóa trạng thái.
Giorgio

12
"Theo đó, các vấn đề phù hợp nhất để giải quyết bằng lập trình chức năng thực chất là các vấn đề toán học.": Tôi không đồng ý với tuyên bố này (xem thêm nhận xét trước đây của tôi), ít nhất tôi không hiểu tại sao nó phải đúng hoặc nó là gì thực sự có nghĩa là. Bạn có thể thấy việc truyền tải cấu trúc thư mục là một vấn đề toán học và thực hiện nó với hàm đệ quy, nhưng vì khách hàng không thấy mã nguồn, bạn chỉ giải quyết một vấn đề thực tế như tìm tệp ở đâu đó trong hệ thống tệp. Tôi tìm thấy một sự phân biệt như vậy giữa các vấn đề toán học và phi toán học nhân tạo.
Giorgio

5
+1, nhưng tôi không được bán ở điểm 2 và 4. Tôi đã thấy rất nhiều bài hướng dẫn và bài đăng trên blog của Haskell bắt đầu bằng cách giải quyết vấn đề từ trên xuống, xác định tất cả các loại và thao tác để xem cách tất cả khớp với nhau trong khi để lại mọi giá trị và định nghĩa hàm không xác định (tốt, được xác định để ném ngoại lệ). Dường như đối với tôi, các lập trình viên Haskell có xu hướng mô hình hóa vấn đề kỹ lưỡng hơn vì họ có xu hướng thêm các loại tầm thường vì lợi ích của ngữ nghĩa - ví dụ: thêm một SafeStringloại để biểu thị các chuỗi đã thoát HTML - và thường theo dõi nhiều hơn Các hiệu ứng.
Doval

4
"các vấn đề phù hợp nhất để giải quyết bằng lập trình hàm thực chất là các vấn đề toán học." nó đơn giản là không đúng. Thực tế là khái niệm về Monads xuất phát từ lý thuyết thể loại cũng không có nghĩa là các nhiệm vụ toán học là miền tự nhiên / phù hợp nhất cho các ngôn ngữ chức năng. Tất cả điều đó có nghĩa là mã được viết bằng các ngôn ngữ chức năng được gõ mạnh có thể được mô tả, phân tích và suy luận về việc sử dụng toán học. Đây là một tính năng bổ sung mạnh mẽ, không phải thứ gì đó ngăn bạn viết "Cuộc phiêu lưu của ngựa Barbie" trong Haskell.
itbruce

6
@itsbruce Barbie Horse Adventures là một mô phỏng toán học nghiêm túc có tầm cỡ cao nhất, nó đưa PTC vào trong các phép chiếu số quá phức tạp như ma trận mô tả sự lung linh vật lý của tóc Barbie trong một thời kỳ rời rạc trong nhiều môi trường thực tế có thể thuyết phục mọi người loại bỏ hoàn toàn nó là không liên quan đến mã hóa vấn đề kinh doanh hàng ngày của họ.
Jimmy Hoffa

7

Tôi muốn nhấn mạnh một khía cạnh mà tôi thấy quan trọng và điều đó không được đề cập trong các câu trả lời khác.

Trước hết, tôi nghĩ rằng khoảng cách đại diện giữa các vấn đề và giải pháp có thể xuất hiện nhiều hơn trong suy nghĩ của lập trình viên, theo nền tảng của họ và theo các khái niệm mà họ quen thuộc hơn.

OOP và FP xem xét dữ liệu và hoạt động từ hai quan điểm khác nhau và cung cấp sự đánh đổi khác nhau, như Robert Harvey đã chỉ ra.

Một khía cạnh quan trọng trong đó chúng khác nhau là cách chúng cho phép mở rộng phần mềm của bạn.

Xem xét tình huống trong đó bạn có một tập hợp các loại dữ liệu và một tập hợp các hoạt động, ví dụ bạn có các định dạng hình ảnh khác nhau và bạn duy trì một thư viện các thuật toán để xử lý hình ảnh.

Lập trình chức năng giúp dễ dàng thêm các hoạt động mới vào phần mềm của bạn: bạn chỉ cần một thay đổi cục bộ trong mã của mình, cụ thể là bạn thêm một chức năng mới, xử lý các định dạng dữ liệu đầu vào khác nhau. Mặt khác, việc thêm các định dạng mới có liên quan nhiều hơn: bạn cần thay đổi tất cả các chức năng bạn đã thực hiện (không thay đổi cục bộ).

Cách tiếp cận hướng đối tượng là đối xứng với điều này: mỗi kiểu dữ liệu mang theo cách thực hiện riêng của tất cả các hoạt động và chịu trách nhiệm chọn việc thực hiện đúng trong thời gian chạy (gửi động). Điều này giúp dễ dàng thêm một loại dữ liệu mới (ví dụ: định dạng hình ảnh mới): bạn chỉ cần thêm một lớp mới và thực hiện tất cả các phương thức của nó. Mặt khác, việc thêm một hoạt động mới có nghĩa là thay đổi tất cả các lớp cần cung cấp hoạt động đó. Trong nhiều ngôn ngữ, điều này được thực hiện bằng cách mở rộng giao diện và điều chỉnh tất cả các lớp thực hiện nó.

Cách tiếp cận khác nhau để mở rộng này là một trong những lý do tại sao OOP thích hợp hơn cho một số vấn đề nhất định trong đó tập hợp các hoạt động thay đổi ít thường xuyên hơn tập hợp các loại dữ liệu mà các hoạt động này hoạt động. Một ví dụ điển hình là GUIs: bạn có một tập cố định các hoạt động mà tất cả các vật dụng phải thực hiện ( paint, resize, move, và vân vân) và một tập hợp các widget mà bạn muốn mở rộng.

Vì vậy, theo kích thước này, OOP và FP chỉ là hai cách tổ chức mã của bạn. Xem SICP , cụ thể là Mục 2.4.3, Bảng 2.22 và thông báo đoạn văn .

Tóm tắt: trong OOP bạn sử dụng dữ liệu để tổ chức các hoạt động, trong FP bạn sử dụng các hoạt động để tổ chức dữ liệu. Mỗi cách tiếp cận mạnh hơn hoặc yếu hơn theo bối cảnh. Nói chung, không ai trong hai người có khoảng cách đại diện cao hơn giữa vấn đề và giải pháp.


1
Trên thực tế, Mô hình khách truy cập (trong OOP) cung cấp cho bạn sức mạnh giống như bạn đã nói với FP. Nó cho phép bạn thêm các hoạt động mới trong khi làm cho việc thêm các lớp mới khó hơn. Tôi tự hỏi nếu bạn có thể làm tương tự trong FP (ví dụ: thêm các định dạng mới thay vì hoạt động).
Euphoric

1
Kịch bản xử lý ảnh dường như không phải là ví dụ tốt nhất. Chắc chắn bạn sẽ chuyển đổi hình ảnh thành một số định dạng nội bộ và xử lý thay vì viết các triển khai riêng biệt của tất cả các công cụ xử lý hình ảnh của bạn cho từng loại hình ảnh?
Này

1
@Giorgio có lẽ tôi không hiểu ý định từ phần này: "thêm các định dạng mới có liên quan nhiều hơn: bạn cần thay đổi tất cả các chức năng bạn đã thực hiện."
Này,

2
@Hey Giorgio có khả năng đề cập đến vấn đề được gọi là Biểu hiện . "Thêm định dạng mới" có nghĩa là "thêm các hàm tạo mới (còn gọi là 'trường hợp') vào kiểu dữ liệu".
Andres F.

1
@Euphoric: Tôi chưa xem xét chi tiết, nhưng đối tác FP với mẫu khách truy cập nên liên quan đến một Otherbiến thể / hàm tạo trong kiểu dữ liệu của bạn và một tham số hàm bổ sung được truyền cho tất cả các hàm để xử lý trường hợp khác . Tất nhiên, đó là vụng về / ít tự nhiên đối với giải pháp OOP (công văn động). Theo cùng một cách, mẫu khách truy cập khó xử / kém tự nhiên hơn so với giải pháp FP (một hàm bậc cao hơn).
Giorgio

5

Hầu hết các ngôn ngữ chức năng không phải là hướng đối tượng. Điều đó không có nghĩa là chúng không có đối tượng (theo nghĩa các loại phức tạp có chức năng cụ thể liên quan đến chúng). Haskell, giống như java, có Danh sách, Bản đồ, Mảng, tất cả các loại Cây và nhiều loại phức tạp khác. Nếu bạn nhìn vào mô-đun Haskell List hoặc Map, bạn sẽ thấy một tập hợp các hàm rất giống với các phương thức trong hầu hết các mô-đun thư viện ngôn ngữ OO tương đương. Nếu bạn kiểm tra mã, bạn thậm chí sẽ tìm thấy sự đóng gói tương tự, với một số loại (hoặc các hàm tạo của chúng, chính xác) và các chức năng chỉ có thể sử dụng được bởi các chức năng khác trong mô-đun.

Cách bạn làm việc với các loại này trong Haskell thường khó khác với cách OO. Trong Haskell tôi nói

  null xs
  length xs

và trong Java bạn nói

  xs.isEmpty
  xs.length

Cà chua, cà chua. Các chức năng Haskell không được gắn với đối tượng nhưng chúng được liên kết chặt chẽ với loại của nó. ¨ À, nhưng , say bạn nói, Java trong Java, nó đang gọi phương thức độ dài nào phù hợp với lớp thực tế của đối tượng đó¨. Bất ngờ - Haskell cũng thực hiện đa hình với các lớp loại (và những thứ khác).

Trong Haskell, nếu tôi có - một tập hợp các số nguyên - nó không quan trọng cho dù bộ sưu tập là một danh sách hoặc bộ hoặc mảng, mã này

  fmap (* 2) is

sẽ trả về một bộ sưu tập với tất cả các yếu tố được nhân đôi. Đa hình có nghĩa là chức năng bản đồ thích hợp cho loại cụ thể sẽ được gọi.

Những ví dụ này, trong thực tế, không phải là loại thực sự phức tạp nhưng cũng được áp dụng tương tự trong các vấn đề khó khăn hơn. Ý tưởng rằng các ngôn ngữ chức năng không cho phép bạn mô hình hóa các đối tượng phức tạp và liên kết chức năng cụ thể với chúng đơn giản là sai.

những khác biệt quan trọng và có ý nghĩa giữa các phong cách chức năng và OO nhưng tôi không nghĩ rằng câu trả lời này có để đối phó với họ. Bạn đã hỏi nếu các ngôn ngữ chức năng ngăn chặn (hoặc cản trở) mô hình hóa trực quan các vấn đề và nhiệm vụ. Câu trả lời là không.


Thực tế, bạn có thể sử dụng các lớp loại trong Java / C #; bạn chỉ cần vượt qua từ điển các hàm một cách rõ ràng, thay vì để trình biên dịch suy ra chính xác dựa trên loại.
Doval

Ồ, chắc chắn rồi. Như William Cook đã nói, nhiều mã OO thực sự sử dụng thường xuyên hơn các hàm bậc cao hơn mã chức năng, mặc dù người viết mã có thể không biết về nó.
itbruce

2
Bạn đang làm cho một sự phân biệt sai. Một danh sách không còn là cấu trúc dữ liệu trơ hơn một ngăn xếp hoặc hàng đợi hoặc giả lập tic-tac-toe. Một danh sách có thể chứa bất cứ thứ gì (trong Haskell, nó có thể được áp dụng một phần chức năng) và xác định cách những thứ đó có thể được tương tác với. Bạn có thể áp dụng một danh sách đầy đủ các hàm được áp dụng một phần cho một danh sách giá trị khác và kết quả sẽ là một danh sách mới chứa mọi tổ hợp hàm có thể có từ đầu tiên và đầu vào từ thứ hai. Đó là nhiều hơn một cấu trúc C.
itbruce

3
Các loại được sử dụng cho nhiều thứ khác nhau trong các ngôn ngữ chức năng hơn các loại bắt buộc / OO (các hệ thống loại có xu hướng mạnh hơn và nhất quán hơn, đối với một thứ). Các loại và chức năng của chúng được sử dụng để định hình các đường dẫn mã (đó là lý do tại sao một số ngôn ngữ chức năng đơn giản là không có từ khóa cho các vòng lặp - chẳng hạn).
itbruce

4
@Fuhrmanator "Tôi không thấy cách này được thực hiện trong FP" Vì vậy, bạn đang phàn nàn điều gì đó mà bạn không hiểu không có ý nghĩa; đi tìm hiểu nó, hiểu nó kỹ lưỡng, và rồi nó sẽ có ý nghĩa. Có thể sau khi nó có ý nghĩa, bạn sẽ quyết định nó là tào lao và bạn không cần người khác chứng minh điều đó với bạn, mặc dù những người khác như trên hiểu điều đó và không đi đến kết luận đó. Dường như bạn đã mang một con dao đến một cuộc đấu súng, và bây giờ bạn đang nói với mọi người rằng súng là vô dụng vì chúng không sắc bén.
Jimmy Hoffa

4

FP thực sự nỗ lực để giảm khoảng cách đại diện:

Một cái gì đó bạn sẽ thấy rất nhiều ngôn ngữ chức năng là việc thực hành xây dựng ngôn ngữ (sử dụng thiết kế từ dưới lên) thành Ngôn ngữ cụ thể miền nhúng (EDSL) . Điều này cho phép bạn phát triển một phương tiện thể hiện mối quan tâm kinh doanh của bạn theo cách tự nhiên cho miền của bạn trong ngôn ngữ lập trình. Haskell và Lisp đều tự hào về điều này.

Một phần (nếu không phải tất cả) của những gì cho phép khả năng này là linh hoạt và biểu cảm hơn trong chính ngôn ngữ cơ sở; với các hàm hạng nhất, các hàm bậc cao hơn, thành phần hàm và trong một số ngôn ngữ, các loại dữ liệu đại số (hiệp hội phân biệt đối xử AKA) và khả năng xác định toán tử *, có thể linh hoạt hơn để thể hiện mọi thứ so với OOP, có nghĩa là bạn có thể sẽ tìm thấy những cách tự nhiên hơn để diễn đạt mọi thứ từ miền vấn đề của bạn. (Cần nói điều gì đó mà nhiều ngôn ngữ OOP đã áp dụng nhiều tính năng này gần đây, nếu không bắt đầu với chúng.)

* Có, trong nhiều ngôn ngữ OOP, bạn có thể ghi đè các toán tử tiêu chuẩn, nhưng trong Haskell, bạn có thể xác định các ngôn ngữ mới!

Theo kinh nghiệm làm việc với các ngôn ngữ chức năng (chủ yếu là F # và Haskell, một số Clojure và một chút Lisp và Erlang), tôi có thời gian dễ dàng hơn trong việc ánh xạ không gian vấn đề vào ngôn ngữ so với OOP - đặc biệt là với các loại dữ liệu đại số, mà tôi thấy linh hoạt hơn các lớp học. Một cái gì đó đã ném tôi vào một vòng lặp khi bắt đầu với FP, đặc biệt là với Haskell, là tôi phải suy nghĩ / làm việc ở mức độ cao hơn một chút so với tôi đã sử dụng trong các ngôn ngữ bắt buộc hoặc OOP; bạn có thể đang chạy vào đây một chút là tốt.


Mối quan tâm kinh doanh (từ khách hàng) không được thể hiện rất thường xuyên trong các chức năng bậc cao. Nhưng một khách hàng có thể viết một câu chuyện người dùng một dòng và cung cấp cho bạn các quy tắc kinh doanh (hoặc bạn có thể đưa họ ra khỏi khách hàng). Tôi quan tâm đến mô hình miền (từ trên xuống?) (Từ những đồ tạo tác đó) cho phép chúng ta đi đến sự trừu tượng của vấn đề ánh xạ tới các hàm hạng nhất, v.v. Bạn có thể trích dẫn một số tài liệu tham khảo không? Bạn có thể sử dụng một ví dụ cụ thể về một tên miền, sự trừu tượng, v.v.?
Fuhrmanator

3
@Fuhrmanator Một khách hàng vẫn có thể viết các câu chuyện người dùng và quy tắc kinh doanh một dòng bất kể bạn có sử dụng OOP và FP hay không . Tôi không mong đợi khách hàng hiểu các chức năng bậc cao hơn bất kỳ điều gì tôi mong họ hiểu về tính kế thừa, thành phần hoặc giao diện.
Andres F.

1
@Fuhrmanator Bạn có thường xuyên có khách hàng bày tỏ mối quan tâm của họ về các khái niệm chung về Java hoặc các lớp và giao diện cơ sở trừu tượng không? Chúa ơi. Paul đã cho bạn một câu trả lời rất hay, giải thích một phương pháp mô hình thực tế và đáng tin cậy bằng cách sử dụng các kỹ thuật mà chắc chắn không quá khó để hình dung. Tuy nhiên, bạn khẳng định rằng nó được mô tả theo một kỹ thuật mô hình cụ thể cho một mô hình cụ thể, như thể đó là cách tự nhiên nhất để thể hiện thiết kế và bất cứ điều gì khác có thể được vẽ lại theo thuật ngữ của nó.
itbruce

@Fuhrmanator Điều này có thể không cao cấp như bạn đang tìm kiếm nhưng nên cung cấp một số cái nhìn sâu sắc về quy trình thiết kế bằng cách sử dụng các kỹ thuật chức năng: Thiết kế với các loại . Tôi khuyên bạn nên duyệt trang web đó nói chung vì nó có một số bài viết tuyệt vời.
paul

@Fuhrmanator Ngoài ra còn có loạt chức năng Tư duy này
paul

3

Như Niklaus Wirth đã nói, "Thuật toán + Cấu trúc dữ liệu = Chương trình". Lập trình hàm là về cách tổ chức các thuật toán và nó không nói nhiều về cách tổ chức cấu trúc dữ liệu. Thật vậy, tồn tại các ngôn ngữ FP cả với các biến có thể thay đổi (Lisp) và bất biến (Haskell, Erlang). Nếu bạn muốn so sánh và đối chiếu FP với một cái gì đó, bạn nên chọn lập trình mệnh lệnh (C, Java) và khai báo (Prolog).

Mặt khác, OOP là một cách để xây dựng các cấu trúc dữ liệu và gắn các thuật toán với chúng. FP không ngăn bạn xây dựng các cấu trúc dữ liệu tương tự như các cấu trúc OOP. Nếu bạn không tin tôi, hãy xem khai báo kiểu Haskell. Tuy nhiên, hầu hết các ngôn ngữ FP đều đồng ý rằng các hàm không thuộc về cấu trúc dữ liệu và nên ở trong cùng một không gian tên. Điều này được thực hiện để đơn giản hóa việc xây dựng các thuật toán mới thông qua thành phần chức năng. Thật vậy, nếu bạn biết đầu vào của hàm là gì và đầu ra của hàm nào, tại sao nó lại thuộc cấu trúc dữ liệu nào? Ví dụ, tại sao addhàm phải được gọi trong một thể hiện của kiểu Integer và truyền một đối số thay vì chỉ đơn giản là được truyền hai đối số Integer?

Do đó, tôi không hiểu tại sao FP nên làm cho các giải pháp khó hiểu hơn nói chung và tôi không nghĩ rằng dù sao đi nữa. Tuy nhiên, hãy nhớ rằng một lập trình viên mệnh lệnh dày dạn chắc chắn sẽ tìm thấy các chương trình chức năng khó hiểu hơn các chương trình bắt buộc và ngược lại. Điều này tương tự với Windows - Phân đôi Linux khi những người đã đầu tư 10 năm để làm quen với môi trường Windows thấy Linux khó khăn sau một tháng sử dụng và những người đã sử dụng Linux có thể đạt được năng suất tương tự trong Windows. Do đó, "khoảng cách đại diện" mà bạn đang nói đến là rất chủ quan.


Do đóng gói và ẩn dữ liệu, không phải là nguyên tắc OO mỗi se, tôi không hoàn toàn đồng ý với OO là cấu trúc dữ liệu + thuật toán (chỉ là chế độ xem nội bộ). Vẻ đẹp của bất kỳ sự trừu tượng tốt nào là có một số dịch vụ mà nó cung cấp để giải quyết một phần của vấn đề, mà không cần phải hiểu quá nhiều chi tiết. Tôi nghĩ rằng bạn đang nói chuyện đóng gói khi bạn tách biệt các chức năng và dữ liệu trong FP, nhưng tôi quá xanh để biết chắc chắn. Ngoài ra, tôi đã chỉnh sửa câu hỏi của mình để đề cập đến việc truy tìm từ giải pháp trở lại vấn đề (để làm rõ ý nghĩa của khoảng cách biểu diễn).
Fuhrmanator

if you know what input a function takes and what output it produces, why should it matter which data structure it belongs too?Nghe có vẻ giống như sự gắn kết, mà đối với bất kỳ hệ thống mô-đun là quan trọng. Tôi lập luận rằng việc không biết tìm một hàm ở đâu sẽ khiến việc hiểu một giải pháp trở nên khó khăn hơn, đó là do khoảng cách biểu diễn cao hơn. Rất nhiều hệ thống OO có vấn đề này, nhưng đó là do thiết kế tồi (độ kết dính thấp). Một lần nữa, vấn đề không gian tên nằm ngoài chuyên môn của tôi về FP, vì vậy có lẽ nó không tệ như âm thanh.
Fuhrmanator

1
@Fuhrmanator Nhưng bạn biết tìm một hàm ở đâu không. Và chỉ có một hàm, không có nhiều hàm có cùng tên, được xác định cho mỗi kiểu dữ liệu (một lần nữa, xem "Bài toán biểu thức"). Ví dụ: đối với các chức năng thư viện Haskell (mà bạn được khuyến khích sử dụng, thay vì khám phá lại bánh xe hoặc tạo một phiên bản ít hữu ích hơn của các chức năng đã nói), bạn có các công cụ khám phá tuyệt vời như Hoogle , rất mạnh mẽ cho phép bạn tìm kiếm bằng chữ ký (thử đi! :))
Andres F.

1
@Fuhrmanator, sau "Nghe có vẻ giống như sự gắn kết", tôi đã mong bạn kết luận một cách hợp lý rằng các ngôn ngữ FP cho phép bạn viết các chương trình với sự gắn kết cao hơn nhưng bạn đã từ bỏ tôi. :-) Tôi nghĩ bạn nên chọn một ngôn ngữ FP, học nó và tự quyết định. Nếu tôi là bạn, tôi khuyên bạn nên bắt đầu với Haskell vì nó khá thuần túy và do đó sẽ không cho phép bạn viết mã theo phong cách bắt buộc. Erlang và một số Lisp cũng là những lựa chọn tốt nhưng chúng pha trộn trong các phong cách lập trình khác. Scala và Clojure, IMO, khá phức tạp để bắt đầu.
mkalkov
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.