Lập trình chức năng, khai báo và mệnh lệnh [đóng]


466

Các thuật ngữ chức năng, khai báo và lập trình mệnh lệnh có nghĩa là gì?


3
Có một số câu trả lời tuyệt vời ở đây. Một điều thú vị không làm sáng tỏ hoàn toàn là khai báocấp bách là bổ sung và cộng sinh, nhiều hơn chỉ là phong cách khác nhau hoặc những gì so với cách .
Bộ

1
@Kit Imo, một số câu trả lời trên trang này đang giới thiệu các điều khoản. DP == minh bạch tham chiếu (RT). DP & IP là đối lập, do đó, afaics không bổ sung cho toàn bộ, tức là toàn bộ chương trình có thể được viết theo một trong hai phong cách. Cuộc gọi đến một chức năng có thể là DP (RT) hoặc IP, việc thực hiện chức năng này có thể là hoặc kết hợp. Chúng không cộng sinh theo nghĩa là một cuộc gọi đến chức năng IP trong một chức năng DP khác có thể thực hiện IP cuộc gọi của chức năng DP. Chúng là cộng sinh theo nghĩa là các chương trình trong thế giới thực (ví dụ như phản ứng chức năng) có thể sử dụng hỗn hợp, ví dụ như các cuộc gọi cấp cao nhất IP vào các chức năng DP.
Shelby Moore III

ahould được thêm vào wiki hoặc một liên kết trên một cái gì đó tương tự như wiki, v.v ... đây là một liên kết tuyệt vời trong wikipedia en.wikipedia.org/wiki/Comparison_of_programming_paradigms
Joe


1
Câu hỏi này đang được thảo luận tại Meta: meta.stackoverflow.com/q/342784/2751851
duplode

Câu trả lời:


262

Tại thời điểm viết bài này, các câu trả lời được bình chọn hàng đầu trên trang này là không chính xác và sai lầm về định nghĩa khai báo so với định nghĩa bắt buộc, bao gồm cả câu trả lời trích dẫn Wikipedia. Một số câu trả lời đang giới thiệu các thuật ngữ theo những cách khác nhau.

Cũng tham khảo giải thích của tôi về lý do tại sao lập trình bảng tính là khai báo, bất kể các công thức làm biến đổi các ô.

Ngoài ra, một số câu trả lời cho rằng lập trình chức năng phải là một tập hợp con của khai báo. Về điểm đó, nó phụ thuộc vào việc chúng ta phân biệt "chức năng" với "thủ tục". Hãy xử lý mệnh lệnh so với khai báo trước.

Định nghĩa biểu thức khai báo

Các chỉ thuộc tính mà có thể có thể phân biệt một tường thuật biểu hiện từ một mệnh lệnh biểu thức là tính minh bạch tham chiếu (RT) của tiểu biểu của nó. Tất cả các thuộc tính khác được chia sẻ giữa cả hai loại biểu thức hoặc xuất phát từ RT.

Ngôn ngữ khai báo 100% (nghĩa là ngôn ngữ trong đó mọi biểu thức có thể là RT) không (trong số các yêu cầu RT khác) cho phép đột biến các giá trị được lưu trữ, ví dụ HTML và hầu hết Haskell.

Định nghĩa biểu thức RT

RT thường được gọi là "không có tác dụng phụ". Thuật ngữ hiệu ứng không có định nghĩa chính xác, vì vậy một số người không đồng ý rằng "không có tác dụng phụ" giống như RT. RT có một định nghĩa chính xác .

Do mọi biểu thức con về mặt khái niệm là một lệnh gọi hàm, RT yêu cầu việc thực hiện một hàm (tức là (các) biểu thức bên trong hàm được gọi) không thể truy cập trạng thái có thể thay đổi bên ngoài hàm (truy cập trạng thái cục bộ có thể thay đổi là được phép). Nói một cách đơn giản, hàm (thực hiện) phải thuần túy .

Định nghĩa hàm thuần

Một chức năng thuần túy thường được cho là "không có tác dụng phụ". Các hiệu ứng thuật ngữ không có định nghĩa chính xác, vì vậy một số người không đồng ý.

Hàm thuần có các thuộc tính sau.

  • đầu ra duy nhất có thể quan sát là giá trị trả về.
  • phụ thuộc đầu ra duy nhất là các đối số.
  • đối số được xác định đầy đủ trước khi bất kỳ đầu ra được tạo ra.

Hãy nhớ rằng RT áp dụng cho các biểu thức (bao gồm các lệnh gọi hàm) và độ tinh khiết áp dụng cho các hàm (triển khai).

Một ví dụ tối nghĩa của các hàm không tinh khiết tạo ra các biểu thức RT là đồng thời, nhưng điều này là do độ tinh khiết bị phá vỡ ở lớp trừu tượng ngắt. Bạn không thực sự cần phải biết điều này. Để thực hiện các biểu thức RT, bạn gọi các hàm thuần túy.

Thuộc tính phái sinh của RT

Bất kỳ thuộc tính nào khác được trích dẫn cho lập trình khai báo, ví dụ trích dẫn từ năm 1999 được Wikipedia sử dụng, xuất phát từ RT hoặc được chia sẻ với lập trình mệnh lệnh. Do đó chứng minh rằng định nghĩa chính xác của tôi là chính xác.

Lưu ý, tính bất biến của các giá trị bên ngoài là tập hợp con của các yêu cầu đối với RT.

  • Các ngôn ngữ khai báo không có cấu trúc điều khiển vòng lặp, ví dụ forwhile, do tính không thay đổi , điều kiện vòng lặp sẽ không bao giờ thay đổi.

  • Các ngôn ngữ khai báo không thể hiện luồng điều khiển ngoài thứ tự hàm lồng nhau (còn gọi là phụ thuộc logic), do tính không thay đổi , các lựa chọn khác của thứ tự đánh giá không thay đổi kết quả (xem bên dưới).

  • Các ngôn ngữ khai báo biểu thị các "bước" logic (nghĩa là thứ tự gọi hàm RT lồng nhau), nhưng liệu mỗi lệnh gọi hàm có phải là một ngữ nghĩa cấp cao hơn (tức là "phải làm gì") không phải là một yêu cầu của lập trình khai báo. Sự khác biệt so với mệnh lệnh là do tính bất biến (nói chung là RT), các "bước" này không thể phụ thuộc vào trạng thái có thể thay đổi, thay vào đó chỉ là thứ tự quan hệ của logic được biểu thị (tức là thứ tự lồng của các lệnh gọi hàm, hay còn gọi là biểu thức con ).

    Ví dụ, đoạn HTML <p>không thể được hiển thị cho đến khi các biểu thức con (tức là thẻ) trong đoạn văn được đánh giá. Không có trạng thái có thể thay đổi, chỉ có một phụ thuộc thứ tự do mối quan hệ logic của phân cấp thẻ (lồng các biểu thức con, là các lệnh gọi hàm tương tự lồng nhau ).

  • Do đó, có thuộc tính phái sinh của tính bất biến (nói chung là RT), biểu thức khai báo đó, chỉ biểu thị các mối quan hệ logic của các bộ phận cấu thành (tức là của các đối số hàm biểu thức phụ) và không phải là mối quan hệ trạng thái có thể thay đổi .

Lệnh đánh giá

Việc lựa chọn thứ tự đánh giá của các biểu thức con chỉ có thể cho kết quả khác nhau khi bất kỳ lệnh gọi hàm nào không phải là RT (tức là hàm không thuần túy), ví dụ một số trạng thái có thể thay đổi bên ngoài của hàm được truy cập trong hàm.

Ví dụ, đưa ra một số biểu thức lồng nhau, ví dụ f( g(a, b), h(c, d) ), đánh giá háo hức và lười biếng của các đối số chức năng sẽ cho kết quả tương tự nếu các chức năng f, ghlà tinh khiết.

Trong khi đó, nếu các hàm f, ghkhông thuần túy, thì sự lựa chọn thứ tự đánh giá có thể cho một kết quả khác.

Lưu ý, các biểu thức lồng nhau là các hàm lồng nhau về mặt khái niệm, vì các toán tử biểu thức chỉ là các hàm gọi hàm giả là tiền tố unary, tiền tố unary hoặc ký hiệu infix nhị phân.

Tiếp tuyến, nếu tất cả các định danh, ví dụ như a, b, c, d, là bất biến ở khắp mọi nơi, tiểu bang bên ngoài để chương trình không thể truy cập (ví dụ: I / O), và không có lớp trừu tượng vỡ, sau đó chức năng luôn tinh khiết.

Nhân tiện, Haskell có một cú pháp khác , f (g a b) (h c d).

Chi tiết đánh giá

Hàm là một chuyển trạng thái (không phải là giá trị được lưu trữ có thể thay đổi) từ đầu vào sang đầu ra. Đối với các thành phần RT của các lệnh gọi đến các hàm thuần túy , thứ tự thực hiện của các chuyển đổi trạng thái này là độc lập. Việc chuyển trạng thái của mỗi lệnh gọi hàm là độc lập với các lệnh khác, do thiếu tác dụng phụ và nguyên tắc là hàm RT có thể được thay thế bằng giá trị được lưu trong bộ nhớ cache của nó . Để sửa chữa một quan niệm sai lầm phổ biến , thành phần đơn nguyên thuần túy luôn luôntuyên bố và RT , mặc dù thực tế là IOđơn nguyên của Haskell được cho là không tinh khiết và do đó bắt buộc phải viết Worldtrạng thái bên ngoài chương trình (nhưng theo nghĩa của cảnh báo bên dưới, tác dụng phụ bị cô lập).

Đánh giá háo hức có nghĩa là các đối số hàm được đánh giá trước khi hàm được gọi và đánh giá lười biếng có nghĩa là các đối số không được đánh giá cho đến khi (và nếu) chúng được truy cập trong hàm.

Định nghĩa : các tham số hàm được khai báo tại trang định nghĩa hàm và các đối số hàm được cung cấp tại vị trí gọi hàm . Biết sự khác biệt giữa tham sốđối số .

Về mặt lý thuyết, tất cả các biểu thức là (a thành phần của) cuộc gọi chức năng, ví dụ như các hằng số được chức năng mà không đầu vào, khai thác unary là chức năng với một đầu vào, khai thác trung tố nhị phân là chức năng với hai đầu vào, nhà thầu là chức năng, và các tuyên bố kiểm soát thậm chí (ví dụ if, for, while) có thể được mô hình hóa với các chức năng. Các thứ tự rằng những lập luận chức năng (Đừng nhầm lẫn với lồng chức năng tự gọi) được đánh giá là không công bố bởi các cú pháp, ví dụ như f( g() )háo hức có thể đánh giá gsau đó ftrên gkết quả 's hoặc nó có thể đánh giá fvà chỉ uể oải đánh giá gkhi kết quả của nó là cần thiết trong phạm vi f.

Hãy cẩn thận, không có ngôn ngữ hoàn chỉnh Turing (nghĩa là cho phép đệ quy không giới hạn) là hoàn toàn khai báo, ví dụ đánh giá lười biếng giới thiệu bộ nhớ và thời gian không xác định. Nhưng những tác dụng phụ này do sự lựa chọn thứ tự đánh giá bị giới hạn ở mức tiêu thụ bộ nhớ, thời gian thực hiện, độ trễ, không kết thúc và độ trễ ngoài do đó đồng bộ hóa bên ngoài.

Lập trình chức năng

Bởi vì lập trình khai báo không thể có các vòng lặp, nên cách duy nhất để lặp lại là đệ quy hàm. Theo nghĩa này, lập trình chức năng có liên quan đến lập trình khai báo.

Nhưng lập trình chức năng không giới hạn ở lập trình khai báo . Thành phần chức năng có thể tương phản với phân nhóm , đặc biệt là đối với Vấn đề Biểu hiện , trong đó có thể đạt được sự mở rộng bằng cách thêm các phân nhóm hoặc phân tách chức năng . Mở rộng có thể là một kết hợp của cả hai phương pháp.

Lập trình hàm thường làm cho hàm trở thành một đối tượng hạng nhất, nghĩa là kiểu hàm có thể xuất hiện trong ngữ pháp ở bất kỳ nơi nào khác. Kết quả cuối cùng là các hàm có thể nhập và hoạt động trên các hàm, do đó cung cấp sự phân tách mối quan tâm bằng cách nhấn mạnh thành phần hàm, tức là tách các phụ thuộc giữa các tính toán con của một tính toán xác định.

Ví dụ, thay vì viết một hàm riêng biệt (và sử dụng đệ quy thay vì các vòng lặp nếu hàm đó cũng phải là khai báo) cho mỗi số lượng vô hạn các hành động chuyên môn có thể áp dụng cho từng phần tử của bộ sưu tập, lập trình hàm sử dụng phép lặp có thể sử dụng lại chức năng, ví dụ như map, fold, filter. Các hàm lặp này nhập vào một hàm hành động chuyên biệt hạng nhất. Các hàm lặp này lặp lại bộ sưu tập và gọi hàm hành động chuyên biệt đầu vào cho mỗi phần tử. Các hàm hành động này ngắn gọn hơn vì chúng không còn cần chứa các câu lệnh lặp để lặp lại bộ sưu tập.

Tuy nhiên, lưu ý rằng nếu một hàm không thuần túy thì đó thực sự là một thủ tục. Có lẽ chúng ta có thể lập luận rằng lập trình chức năng sử dụng các hàm không tinh khiết, thực sự là lập trình thủ tục. Do đó, nếu chúng ta đồng ý rằng các biểu thức khai báo là RT, thì chúng ta có thể nói rằng lập trình thủ tục không phải là lập trình khai báo, và do đó chúng ta có thể lập luận rằng lập trình hàm luôn là RT và phải là một tập hợp con của lập trình khai báo.

Song song

Thành phần chức năng này với các chức năng hạng nhất có thể biểu thị độ sâu trong sự song song bằng cách tách ra chức năng độc lập.

Nguyên tắc của Brent: tính toán với công việc w và độ sâu d có thể được thực hiện trong PRAM bộ xử lý p trong thời gian O (max (w / p, d)).

Cả đồng thời và song song cũng yêu cầu lập trình khai báo , tức là bất biến và RT.

Vậy giả định nguy hiểm này mà Parallelism == Đồng thời đến từ đâu? Đó là hậu quả tự nhiên của các ngôn ngữ có tác dụng phụ: khi ngôn ngữ của bạn có tác dụng phụ ở mọi nơi, thì bất cứ lúc nào bạn cố gắng làm nhiều việc một lúc, về cơ bản bạn không có tính quyết định gây ra bởi các hiệu ứng xen kẽ từ mỗi thao tác . Vì vậy, trong các ngôn ngữ có hiệu lực phụ, cách duy nhất để có được sự song song là đồng thời; Do đó, không có gì đáng ngạc nhiên khi chúng ta thường thấy hai người bị giam cầm.

Lệnh đánh giá FP

Lưu ý thứ tự đánh giá cũng tác động đến tác dụng phụ chấm dứt và hiệu suất của thành phần chức năng.

Háo hức (CBV) và lười biếng (CBN) là các cuộc đấu tay đôi phân loại [ 10 ], bởi vì chúng đã đảo ngược thứ tự đánh giá, tức là liệu các chức năng bên ngoài hay bên trong được đánh giá đầu tiên. Hãy tưởng tượng một cây lộn ngược, sau đó háo hức đánh giá từ các nhánh cây chức năng lên phân cấp nhánh đến thân cây hàm cấp cao nhất; trong khi đó, lười đánh giá từ thân cây đến đầu cành. Eager không có các sản phẩm kết hợp ("và", a / k / a "sản phẩm" phân loại và lười biếng không có các sản phẩm sao chép rời rạc ("hoặc", a / k / a "tổng hợp") [ 11 ].

Hiệu suất

  • Hăng hái

    Cũng như không kết thúc, háo hức quá háo hức với thành phần chức năng kết hợp, tức là cấu trúc điều khiển thành phần thực hiện những công việc không cần thiết không được thực hiện với sự lười biếng. Ví dụ , háo hức và háo hức ánh xạ toàn bộ danh sách thành booleans, khi nó được tạo với một nếp gấp chấm dứt trên phần tử thực đầu tiên.

    Công việc không cần thiết này là nguyên nhân của yếu tố "lên đến" được yêu cầu thêm trong độ phức tạp thời gian tuần tự của háo hức so với lười biếng, cả hai đều có chức năng thuần túy. Một giải pháp là sử dụng functor (ví dụ: danh sách) với các hàm tạo lười biếng (nghĩa là háo hức với các sản phẩm lười tùy chọn), bởi vì với sự háo hức, sự háo hức không chính xác bắt nguồn từ chức năng bên trong. Điều này là do các sản phẩm là loại xây dựng, tức là loại quy nạp với đại số ban đầu trên một điểm cố định ban đầu [ 11 ]

  • Lười biếng

    Cũng như không chấm dứt, lười biếng quá lười biếng với thành phần chức năng rời rạc, tức là tính hữu hạn cưỡng chế có thể xảy ra muộn hơn mức cần thiết, dẫn đến cả công việc không cần thiết và không xác định độ trễ không phải là trường hợp háo hức [ 10 ] [ 11 ] . Ví dụ về tính hữu hạn là các ngoại lệ về trạng thái, thời gian, không kết thúc và thời gian chạy. Đây là những tác dụng phụ bắt buộc, nhưng ngay cả trong một ngôn ngữ khai báo thuần túy (ví dụ Haskell), vẫn có trạng thái trong đơn vị IO bắt buộc (lưu ý: không phải tất cả các đơn vị đều bắt buộc!) Ẩn trong phân bổ không gian và thời gian là trạng thái liên quan đến mệnh lệnh thế giới thực. Sử dụng lười biếng ngay cả với các bản sao háo hức tùy chọn rò rỉ "sự lười biếng" vào các bản sao bên trong, bởi vì với sự lười biếng, sự lười biếng không chính xác bắt nguồn từ chức năng bên ngoài(xem ví dụ trong phần Không kết thúc, trong đó == là hàm toán tử nhị phân bên ngoài). Điều này là do các bản sao được giới hạn bởi tính hữu hạn, tức là các kiểu cưỡng chế với đại số cuối cùng trên một đối tượng cuối cùng [ 11 ].

    Sự lười biếng gây ra sự không xác định trong thiết kế và gỡ lỗi các chức năng cho độ trễ và không gian, việc gỡ lỗi có lẽ vượt quá khả năng của phần lớn các lập trình viên, vì sự bất đồng giữa hệ thống phân cấp chức năng được khai báo và thứ tự đánh giá thời gian chạy. Các hàm thuần túy lười biếng được đánh giá với sự háo hức, có khả năng có thể giới thiệu không kết thúc chưa từng thấy trước đây trong thời gian chạy. Ngược lại, các hàm thuần túy háo hức được đánh giá với sự lười biếng, có khả năng có thể giới thiệu không gian vô hình và độ trễ không xác định trước đó trong thời gian chạy.

Không chấm dứt

Tại thời điểm biên dịch, do sự cố Dừng và đệ quy lẫn nhau trong một ngôn ngữ hoàn chỉnh Turing, các chức năng thường không thể được đảm bảo để chấm dứt.

  • Hăng hái

    Với sự háo hức nhưng không lười biếng, vì sự kết hợp của Head"và" Tail, nếu một trong hai Headhoặc Tailkhông chấm dứt, thì tương ứng List( Head(), Tail() ).tail == Tail()hoặc List( Head(), Tail() ).head == Head()là không đúng bởi vì bên trái không, và bên phải không, chấm dứt.

    Trong khi đó, với sự lười biếng cả hai bên chấm dứt. Do đó, háo hức quá háo hức với các sản phẩm kết hợp và không kết thúc (bao gồm cả ngoại lệ thời gian chạy) trong những trường hợp không cần thiết.

  • Lười biếng

    Với sự lười biếng nhưng không háo hức, vì sự phân biệt của 1"hoặc" 2, nếu fkhông chấm dứt, thì điều đó List( f ? 1 : 2, 3 ).tail == (f ? List( 1, 3 ) : List( 2, 3 )).taillà không đúng bởi vì phía bên trái chấm dứt và bên phải thì không.

    Trong khi đó, với sự háo hức không bên nào chấm dứt nên bài kiểm tra bình đẳng không bao giờ đạt được. Do đó, lười biếng là quá lười biếng với các sản phẩm sao chép rời rạc, và trong những trường hợp đó không thể chấm dứt (bao gồm các ngoại lệ thời gian chạy) sau khi thực hiện nhiều công việc hơn háo hức.

[ 10 ] Tiếp tục khai báo và tính đối ngẫu phân loại, Filinski, phần 2.5.4 So sánh CBV và CBN, và 3.6.1 CBV và CBN trong SCL.

[ 11 ] Declarative continuations và Categorical Duality, Filinski, phần 2.2.1 Sản phẩm và coproducts, 2.2.2 Terminal và đối tượng ban đầu, 2.5.2 CBV với các sản phẩm lười biếng, và 2.5.3 CBN với coproducts háo hức.


Ngay cả với lập trình ràng buộc khai báo, các ràng buộc không biến đổi trong khi người giải đang tìm giải pháp. Điều này là hiển nhiên bởi vì không có cách nào để xác định thời gian để họ thay đổi. Ngay cả các ràng buộc đã chỉ định, các ràng buộc khác đều được nêu trước khi bộ giải được chạy để tìm giải pháp. Điều này tương tự với các công thức khai báo trong bảng tính .
Shelby Moore III

3
Viết tắt không có nghĩa là cung cấp một định nghĩa. Trong đó tôi đã viết "RT thường được viết tắt là 'không có tác dụng phụ'", điều đó không có nghĩa là định nghĩa của RT là "không có tác dụng phụ", bởi vì mọi người có thể có các định nghĩa khác nhau về 'hiệu ứng'. Thay vào đó, nếu tôi nói "RT thường được viết tắt là 'xyz'", một biểu tượng vô nghĩa sẽ không đưa ra bất kỳ định nghĩa nào cho RT. RT có một định nghĩa chính xác không bao giờ thay đổi, bất kể người ta sử dụng biểu tượng nào để chỉ nó.
Shelby Moore III

Tôi không thể tìm thấy một ví dụ ngược lại với tuyên bố của tôi rằng mọi loại DP đều là RT. Ví dụ: ý nghĩa (nghĩa là giá trị) của các thuật ngữ của ngữ pháp nhạy cảm ngữ cảnh không biến đổi tại một thời điểm hoặc vị trí khác nhau trong ngữ pháp. Xem bình luận lập trình ràng buộc của tôi ở trên.
Shelby Moore III

1
Việc đánh đồng C theo kiểu ESP với RT trong trạng thái đơn nguyên, là không hợp lệ , bởi vì mỗi câu lệnh C có thể làm thay đổi trạng thái toàn cầu, trong khi "bên trong" trạng thái đơn nguyên, mỗi câu lệnh tương ứng sẽ tạo ra một BẢN SAO của trạng thái (đã được sửa đổi). Cái sau là RT-- trước đây thì không. Thành phần đơn nguyên luôn là RT. DP == RT là ý nghĩa duy nhất cho DP là một tập hợp các thuộc tính rời rạc (bằng chứng toán học tôi đúng, DP khác là vô nghĩa).
Shelby Moore III

1
Tôi ước tôi có thể hiểu điều này qua câu đầu tiên. Tôi đã đọc hướng dẫn sử dụng DAX chỉ ra đó là "ngôn ngữ chức năng". Điều đó có nghĩa là gì? Tôi không biết hãy hỏi pop của bạn.
Nick.McDilyn

103

Thực sự không có bất kỳ định nghĩa khách quan, không mơ hồ cho những điều này. Đây là cách tôi sẽ định nghĩa chúng:

Bắt buộc - Trọng tâm là những gì máy tính nên thực hiện thay vì những gì máy tính sẽ làm (ví dụ: C, C ++, Java).

Tuyên bố - Trọng tâm là những gì máy tính nên làm hơn là cách nó nên làm (ví dụ: SQL).

Chức năng - một tập hợp các ngôn ngữ khai báo tập trung nhiều vào đệ quy


1
Hãy ghi nhớ một số điều: 1) giải thích được dự định đơn giản hơn là bao gồm 2) như tôi đã nói, có nhiều cách để định nghĩa các ngôn ngữ này. Vì vậy, câu trả lời rất có thể sai với bạn và đúng với người khác.
Jason Baker

3
Lập trình hàm không phải là "tập hợp con của ngôn ngữ khai báo". Lập trình khai báo yêu cầu tính bất biến của các giá trị được lưu trữ, lập trình hàm không, nếu nó không phải là FP thuần túy . Xem câu trả lời của tôi . Xem thêm giải thích cho các ô bảng tính . Các định nghĩa khách quan chính xác không "mơ hồ". Lập trình bắt buộc cũng tập trung vào "những gì máy tính nên làm". Sự khác biệt duy nhất là lập trình bắt buộc phải đối phó với các giá trị được lưu trữ có thể thay đổi.
Shelby Moore III

5
@ShelbyMooreIII - Tôi có xu hướng đồng ý với Eric Meijer về điều này. Thực sự không có thứ gọi là "ngôn ngữ chức năng không thuần túy". Theo như tôi quan tâm, Ocaml, F # và những thứ tương tự là những ngôn ngữ bắt buộc có cấu trúc dữ liệu chức năng. Nhưng như tôi đã nói trong câu trả lời của mình, tôi không tin có câu trả lời khách quan, không mơ hồ nào cho câu hỏi này. Có nhiều cách định nghĩa mọi thứ.
Jason Baker

3
Về mặt toán học, người ta có thể chứng minh rằng anh ta đang hiểu các thuật ngữ, khi không có định nghĩa nào rõ ràng, bởi vì các thuộc tính được chọn không phải là một tập hợp rời rạc. Nếu bạn định nghĩa FP chỉ là FP thuần túy (tức là RT), thì nó không khác biệt với DP, cf Câu trả lời của tôi . Các thuộc tính rời rạc của FP bao gồm loại hàm hạng nhất, có thể là một hàm bắt buộc. Tôi tìm thấy nhiều điều khoản cơ bản mơ hồ ở đâyđây . Ưu tiên FP thuần túy là trực giao với định nghĩa của chỉ FP.
Shelby Moore III

21
@ShelbyMooreIII - Tôi đã đưa ra giả định rằng OP muốn câu trả lời của anh ấy bằng tiếng Anh, không phải Math Nerd-ese. Nếu đó là một giả định không hợp lệ, thì lời xin lỗi của tôi.
Jason Baker

54

mệnh lệnhkhai báo mô tả hai phong cách lập trình đối lập. bắt buộc là cách tiếp cận "công thức từng bước" truyền thống trong khi khai báo là "đây là điều tôi muốn, bây giờ bạn tìm ra cách thực hiện".

hai cách tiếp cận này xảy ra trong suốt chương trình - ngay cả với cùng một ngôn ngữ và cùng một chương trình. Nói chung, cách tiếp cận khai báo được coi là thích hợp hơn, bởi vì nó giải phóng lập trình viên khỏi việc chỉ định quá nhiều chi tiết, trong khi cũng có ít cơ hội hơn cho các lỗi (nếu bạn mô tả kết quả bạn muốn và một số quy trình tự động được kiểm tra tốt có thể hoạt động ngược từ đó đến xác định các bước sau đó bạn có thể hy vọng rằng mọi thứ đáng tin cậy hơn là phải chỉ định từng bước một).

mặt khác, một cách tiếp cận bắt buộc mang lại cho bạn quyền kiểm soát ở mức độ thấp hơn - đó là "phương pháp vi mô" để lập trình. và điều đó có thể cho phép lập trình viên khai thác kiến ​​thức về vấn đề để đưa ra câu trả lời hiệu quả hơn. do đó, không có gì lạ khi một số phần của chương trình được viết theo phong cách khai báo hơn, nhưng đối với các phần quan trọng về tốc độ thì bắt buộc hơn.

như bạn có thể tưởng tượng, ngôn ngữ bạn sử dụng để viết chương trình ảnh hưởng đến cách bạn có thể khai báo - một ngôn ngữ được tích hợp "thông minh" để tìm ra những gì cần làm được mô tả về kết quả sẽ cho phép khai báo nhiều hơn tiếp cận hơn một nơi mà lập trình viên trước tiên cần thêm loại trí thông minh đó bằng mã bắt buộc trước khi có thể xây dựng một lớp khai báo nhiều hơn trên đầu trang. vì vậy, ví dụ, một ngôn ngữ như prolog được coi là rất khai báo bởi vì nó có, một quá trình tìm kiếm câu trả lời.

cho đến nay, bạn sẽ nhận thấy rằng tôi đã không đề cập đến lập trình chức năng . đó là bởi vì đó là một thuật ngữ mà ý nghĩa của nó không liên quan ngay lập tức đến hai cái kia. đơn giản nhất, lập trình chức năng có nghĩa là bạn sử dụng các chức năng. cụ thể là bạn sử dụng ngôn ngữ hỗ trợ các hàm làm "giá trị hạng nhất" - điều đó có nghĩa là bạn không chỉ có thể viết các hàm mà còn có thể viết các hàm viết các hàm (viết các hàm đó ...) và truyền các hàm cho chức năng. Nói tóm lại - các hàm đó linh hoạt và phổ biến như những thứ như chuỗi và số.

Sau đó, nó có vẻ kỳ lạ, rằng chức năng, mệnh lệnh và khai báo thường được đề cập cùng nhau. lý do cho điều này là một hệ quả của việc đưa ý tưởng về lập trình chức năng "đến mức cực đoan". một hàm, theo nghĩa thuần túy nhất của nó, là một thứ gì đó từ toán học - một loại "hộp đen" lấy một số đầu vào và luôn cho cùng một đầu ra. và loại hành vi đó không yêu cầu lưu trữ các biến thay đổi. Vì vậy, nếu bạn thiết kế một ngôn ngữ lập trình với mục đích thực hiện một ý tưởng rất thuần túy, chịu ảnh hưởng toán học của lập trình chức năng, thì cuối cùng bạn sẽ từ chối, phần lớn, ý tưởng về các giá trị có thể thay đổi (theo một nghĩa nhất định, hạn chế, kỹ thuật).

và nếu bạn làm điều đó - nếu bạn giới hạn cách các biến có thể thay đổi - thì gần như tình cờ bạn sẽ buộc lập trình viên phải viết các chương trình mang tính khai báo nhiều hơn, bởi vì một phần lớn của lập trình mệnh lệnh là mô tả cách các biến thay đổi và bạn không còn có thể làm điều đó! Vì vậy, nó chỉ ra rằng lập trình chức năng - đặc biệt, lập trình bằng ngôn ngữ chức năng - có xu hướng đưa ra nhiều mã khai báo hơn.

để tóm tắt, sau đó:

  • mệnh lệnh và khai báo là hai phong cách lập trình đối lập nhau (cùng tên được sử dụng cho các ngôn ngữ lập trình khuyến khích các phong cách đó)

  • lập trình chức năng là một phong cách lập trình trong đó các chức năng trở nên rất quan trọng và do đó, việc thay đổi các giá trị trở nên ít quan trọng hơn. khả năng hạn chế để xác định các thay đổi trong các giá trị buộc một kiểu khai báo hơn.

vì vậy "lập trình chức năng" thường được mô tả là "khai báo".


5
Giải thích tốt nhất cho đến nay. Có vẻ như Chức năng và OOP là trực giao với mệnh lệnh và Tuyên bố.
Didier A.

Bạn có thể nói Lập trình Logic là Tuyên bố? Hay là chính nó trực giao?
Didier A.

51

Tóm lại:

Một ngôn ngữ bắt buộc chỉ định một loạt các hướng dẫn mà máy tính thực hiện theo trình tự (làm điều này, sau đó làm điều đó).

Một ngôn ngữ khai báo khai báo một tập hợp các quy tắc về kết quả đầu ra nào sẽ xuất phát từ đầu vào nào (ví dụ: nếu bạn có A, thì kết quả là B). Một động cơ sẽ áp dụng các quy tắc này cho đầu vào và đưa ra đầu ra.

Một ngôn ngữ chức năng khai báo một tập hợp các hàm toán học / logic xác định cách dịch đầu vào thành đầu ra. ví dụ. f (y) = y * y. nó là một loại ngôn ngữ khai báo.


1
Lập trình hàm không phải là "một loại ngôn ngữ khai báo". Lập trình khai báo đòi hỏi sự bất biến của các giá trị được lưu trữ, lập trình hàm không tinh khiết thì không. Xem câu trả lời của tôi . Xem thêm giải thích cho các ô bảng tính . Các chỉ lý do lý bắt buộc (aka hướng dẫn) thực hiện theo thứ tự là do sự hiện diện của các giá trị được lưu trữ có thể thay đổi, kết quả là phụ thuộc vào trình tự đánh giá. Sử dụng vốn từ vựng của bạn, một "hướng dẫn" có thể (và "quy tắc" không thể) hoạt động trên các giá trị có thể thay đổi.
Shelby Moore III

23

Bắt buộc: làm thế nào để đạt được mục tiêu của chúng tôi

   Take the next customer from a list.
   If the customer lives in Spain, show their details.
   If there are more customers in the list, go to the beginning

Tuyên bố: những gì chúng ta muốn đạt được

   Show customer details of every customer living in Spain

Bạn đang mô tả lập trình chức năng so với phi FP, không phải lập trình so với lập trình mệnh lệnh. Lập trình hàm là trực giao với sự phân cực giữa lập trình mệnh lệnh và khai báo. Lập trình khai báo đòi hỏi sự bất biến của các giá trị được lưu trữ, lập trình hàm không tinh khiết thì không. Xem câu trả lời của tôi .
Shelby Moore III

22

Lập trình mệnh lệnh có nghĩa là bất kỳ phong cách lập trình nào trong đó chương trình của bạn được cấu trúc theo các hướng dẫn mô tả cách các hoạt động được thực hiện bởi máy tính sẽ xảy ra .

Lập trình khai báo có nghĩa là bất kỳ phong cách lập trình nào trong đó chương trình của bạn là một mô tả về vấn đề hoặc giải pháp - nhưng không nói rõ cách thức công việc sẽ được thực hiện .

Lập trình hàm là lập trình bằng cách đánh giá các hàm và hàm của hàm ... Vì lập trình hàm (được định nghĩa nghiêm ngặt) có nghĩa là lập trình bằng cách định nghĩa các hàm toán học miễn phí có hiệu lực phụ nên nó là một dạng lập trình khai báo nhưng nó không phải là loại lập trình khai báo duy nhất .

Lập trình logic (ví dụ trong Prolog) là một dạng lập trình khai báo khác. Nó liên quan đến tính toán bằng cách quyết định xem một tuyên bố logic có đúng không (hoặc liệu nó có thể được thỏa mãn hay không). Chương trình này thường là một chuỗi các sự kiện và quy tắc - tức là một mô tả chứ không phải là một loạt các hướng dẫn.

Viết lại thuật ngữ (ví dụ CASL) là một hình thức lập trình khai báo khác. Nó liên quan đến chuyển đổi biểu tượng của các thuật ngữ đại số. Nó hoàn toàn khác biệt với lập trình logic và lập trình chức năng.


Lập trình hàm không phải là "một dạng lập trình khai báo". Lập trình khai báo đòi hỏi sự bất biến của các giá trị được lưu trữ, lập trình hàm không tinh khiết thì không. Xem câu trả lời của tôi . Xem thêm giải thích cho các ô bảng tính . Thuật ngữ "công việc" trong "mô tả cách thực hiện công việc" không được xác định. Các chỉ lý do lý bắt buộc (aka "hướng dẫn") thực hiện theo thứ tự là do sự hiện diện của các giá trị được lưu trữ có thể thay đổi, kết quả là phụ thuộc vào trình tự đánh giá.
Shelby Moore III

2
Xin vui lòng đọc nó như tôi đã nói về lập trình chức năng thuần túy . Những mô hình này có thể vượt qua và tôi không muốn bị sa lầy vào việc so sánh các ngôn ngữ lai. Về lý thuyết, ít nhất lập trình chức năng là về các chức năng thay vì mô tả cách máy tính sẽ thực hiện mỗi tính toán - vì vậy tôi duy trì nó là khai báo.
Dafydd Rees

Tôi đã chỉnh sửa câu trả lời của mình và trong phần "Lập trình chức năng", tôi đã thêm một kịch bản trong đó chúng ta có thể lập luận rằng FP luôn thuần túy và FP không thực sự là "lập trình thủ tục". Xin lỗi vì không bao gồm giải thích đó sớm hơn.
Shelby Moore III

13

mệnh lệnh - biểu thức mô tả chuỗi hành động cần thực hiện (kết hợp)

khai báo - biểu thức là các khai báo đóng góp vào hành vi của chương trình (kết hợp, giao hoán, bình dị, đơn điệu)

chức năng - biểu thức có giá trị như hiệu ứng duy nhất; ngữ nghĩa hỗ trợ lý luận phương trình


1
Biểu thức khai báo đóng góp vào hành vi dự định của chương trình, mệnh lệnh có thể đóng góp cho dự định hoặc ngoài ý muốn. Tuyên bố không cần phải giao hoán và bình thường, nếu đây là ngữ nghĩa có chủ ý. Tôi thích bản chất ngắn gọn của bạn về chức năng, vì vậy tôi đã nâng cao nó.
Shelby Moore III

10

Vì tôi đã viết câu trả lời trước của mình, tôi đã đưa ra một định nghĩa mới về tài sản khai báo được trích dẫn dưới đây. Tôi cũng đã định nghĩa lập trình mệnh lệnh là thuộc tính kép.

Định nghĩa này là tốt hơn so với câu tôi đã cung cấp trong câu trả lời trước của tôi, bởi vì nó ngắn gọn và nó khái quát hơn. Nhưng nó có thể khó khăn hơn để mò mẫm, bởi vì hàm ý của các định lý không hoàn chỉnh áp dụng cho lập trình và cuộc sống nói chung rất khó để con người quấn lấy tâm trí của họ.

Các giải thích được trích dẫn của định nghĩa thảo luận về vai trò của lập trình chức năng thuần túy trong lập trình khai báo.

Tất cả các loại lập trình kỳ lạ phù hợp với phân loại sau đây của khai báo so với mệnh lệnh, vì định nghĩa sau đây khẳng định chúng là đối ngẫu.

Tuyên bố so với mệnh lệnh

Thuộc tính khai báo là kỳ lạ, khó hiểu và khó nắm bắt trong một định nghĩa chính xác về mặt kỹ thuật vẫn còn chung chung và không mơ hồ, bởi vì đó là một khái niệm ngây thơ rằng chúng ta có thể tuyên bố ý nghĩa (còn gọi là ngữ nghĩa) của chương trình mà không gây ra tác dụng phụ ngoài ý muốn. Có một sự căng thẳng cố hữu giữa việc thể hiện ý nghĩa và tránh các tác động ngoài ý muốn, và sự căng thẳng này thực sự xuất phát từ các định lý không hoàn chỉnh của lập trình và vũ trụ của chúng ta.

Đó là sự đơn giản hóa, về mặt kỹ thuật không chính xác, và thường không rõ ràng để xác định tường thuật như phải làm gì và bắt buộc như làm thế nào để làm . Một trường hợp không rõ ràng là người Viking, người là người thế nào trong một chương trình tạo ra một chương trình, một trình biên dịch.

Rõ ràng là đệ quy không giới hạn làm cho một ngôn ngữ Turing hoàn chỉnh , cũng tương tự trong ngữ nghĩa, không chỉ trong cấu trúc cú pháp đánh giá (còn gọi là ngữ nghĩa hoạt động). Đây là một ví dụ về mặt logic tương tự như định lý của Gôdel, bất kỳ hệ thống tiên đề hoàn chỉnh nào cũng không nhất quán . Suy ngẫm về sự kỳ lạ mâu thuẫn của trích dẫn đó! Nó cũng là một ví dụ chứng minh làm thế nào biểu thức ngữ nghĩa không có ràng buộc có thể chứng minh được, do đó chúng ta không thể chứng minh 2 rằng một chương trình (và tương tự như ngữ nghĩa của nó) dừng lại là định lý Dừng.

Các định lý không hoàn chỉnh xuất phát từ bản chất cơ bản của vũ trụ của chúng ta, như đã nêu trong Định luật Nhiệt động lực học thứ hai là entropy (hay còn gọi là # khả năng độc lập) đang có xu hướng tối đa mãi mãi . Việc mã hóa và thiết kế chương trình không bao giờ kết thúc được. Nó còn sống! - bởi vì nó cố gắng giải quyết nhu cầu của thế giới thực, và ngữ nghĩa của thế giới thực luôn thay đổi và có xu hướng nhiều khả năng hơn. Con người không bao giờ ngừng khám phá những điều mới (bao gồm cả lỗi trong các chương trình ;-).

Để nắm bắt chính xác và kỹ thuật khái niệm mong muốn đã nói ở trên trong vũ trụ kỳ lạ không có lợi thế này sâu sắc.

Định nghĩa:


Thuộc tính khai báo là nơi chỉ có thể tồn tại một tập hợp các câu lệnh có thể biểu thị từng ngữ nghĩa mô-đun cụ thể.

Thuộc tính mệnh lệnh 3 là thuộc tính kép, trong đó ngữ nghĩa không nhất quán theo thành phần và / hoặc có thể được thể hiện bằng các biến thể của các bộ câu lệnh.


Định nghĩa khai báo này đặc biệt cục bộ trong phạm vi ngữ nghĩa, có nghĩa là nó yêu cầu một ngữ nghĩa mô-đun duy trì ý nghĩa nhất quán của nó bất kể nó được khởi tạo và sử dụng trong phạm vi toàn cầu ở đâu và như thế nào . Do đó, mỗi ngữ nghĩa mô-đun khai báo phải trực giao với tất cả những người khác có thể khác và không phải là một thuật toán toàn cầu (do các định lý không hoàn hảo) để chứng kiến ​​tính nhất quán, cũng là điểm của Thay vì không phải lúc nào cũng tốt hơn bởi Robert Harper, Giáo sư Khoa học máy tính tại Đại học Carnegie Mellon, một trong những nhà thiết kế của Standard ML.

Ví dụ về các ngữ nghĩa tường thuật mô-đun bao gồm loại functors lý thuyết, ví dụ như cácApplicative , gõ danh nghĩa, không gian tên, tên trường này, và wrt để cấp độ hoạt động của ngữ nghĩa sau đó lập trình chức năng thuần túy.

Do đó, các ngôn ngữ khai báo được thiết kế tốt có thể diễn đạt rõ ràng hơn ý nghĩa , mặc dù có một số mất tính tổng quát trong những gì có thể diễn đạt, nhưng đạt được những gì có thể được thể hiện với tính nhất quán nội tại.

Một ví dụ về định nghĩa đã nói ở trên là tập hợp các công thức trong các ô của chương trình bảng tính mà không mong đợi sẽ có cùng ý nghĩa khi được chuyển đến các ô cột và hàng khác nhau, tức là các định danh ô đã thay đổi. Các định danh ô là một phần và không thừa đối với ý nghĩa dự định. Vì vậy, mỗi kết quả bảng tính là wrt duy nhất cho các định danh ô trong một tập hợp các công thức. Ngữ nghĩa mô đun nhất quán trong trường hợp này là sử dụng các định danh ô làm đầu vào và đầu ra của các hàm thuần cho các công thức ô (xem bên dưới).

Ngôn ngữ đánh dấu siêu văn bản hay còn gọi là HTML. Ngôn ngữ cho các trang web tĩnh là một ví dụ về ngôn ngữ khai báo cao (nhưng không hoàn hảo 3 ) mà (ít nhất là trước HTML 5) không có khả năng diễn đạt hành vi động. HTML có lẽ là ngôn ngữ dễ học nhất. Đối với hành vi động, một ngôn ngữ kịch bản bắt buộc như JavaScript thường được kết hợp với HTML. HTML không có JavaScript phù hợp với định nghĩa khai báo vì mỗi loại danh nghĩa (nghĩa là các thẻ) duy trì ý nghĩa nhất quán của nó theo thành phần trong các quy tắc của cú pháp.

Một định nghĩa cạnh tranh đối với khai báo là giao hoánidempotent tính chất của những điều khoản về ngữ nghĩa, tức là giấy chi tiết có thể được sắp xếp lại và lặp lại mà không thay đổi ý nghĩa. Ví dụ: các câu lệnh gán giá trị cho các trường được đặt tên có thể được sắp xếp lại và sao chép mà không thay đổi ý nghĩa của chương trình, nếu các tên đó được mô đun hóa theo bất kỳ thứ tự ngụ ý nào. Các tên đôi khi ngụ ý một đơn đặt hàng, ví dụ như số nhận dạng ô bao gồm vị trí cột và hàng của chúng, việc di chuyển tổng số trên bảng tính sẽ thay đổi ý nghĩa của nó. Mặt khác, các thuộc tính này hoàn toàn yêu cầu toàn cầutính nhất quán của ngữ nghĩa. Nhìn chung, không thể thiết kế ngữ nghĩa của các câu lệnh sao cho chúng vẫn nhất quán nếu được sắp xếp ngẫu nhiên hoặc trùng lặp, bởi vì thứ tự và trùng lặp là nội tại đối với ngữ nghĩa. Ví dụ, các câu lệnh Foo Foo tồn tại (và xây dựng) và Foo Foo không tồn tại (và phá hủy). Nếu người ta xem xét sự không nhất quán ngẫu nhiên về mặt hóa học của ngữ nghĩa dự định, thì người ta chấp nhận định nghĩa này là đủ chung cho thuộc tính khai báo. Về bản chất, định nghĩa này là không rõ ràng như một định nghĩa tổng quát vì nó cố gắng tạo sự nhất quán trực giao với ngữ nghĩa, nghĩa là thách thức thực tế rằng vũ trụ của ngữ nghĩa là không bị ràng buộc và không thể bị bắt trong một mô hình kết hợp toàn cầu .

Yêu cầu các đặc tính giao hoán và không cần thiết cho (thứ tự đánh giá cấu trúc của) ngữ nghĩa hoạt động cấp thấp hơn chuyển đổi ngữ nghĩa hoạt động thành một ngữ nghĩa mô đun cục bộ khai báo , ví dụ lập trình hàm thuần túy (bao gồm cả đệ quy thay vì các vòng lặp bắt buộc). Sau đó, thứ tự hoạt động của các chi tiết thực hiện không ảnh hưởng (tức là lan rộng ra toàn cầu ) tính nhất quán của ngữ nghĩa cấp cao hơn. Ví dụ, thứ tự đánh giá (và về mặt lý thuyết cũng là sự trùng lặp) của các công thức bảng tính không thành vấn đề vì các đầu ra không được sao chép vào các đầu vào cho đến khi tất cả các đầu ra đã được tính toán, nghĩa là tương tự với các hàm thuần túy.

C, Java, C ++, C #, PHP và JavaScript không đặc biệt khai báo. Cú pháp của Copute và cú pháp của Python được kết hợp nhiều hơn với các kết quả dự định , nghĩa là ngữ nghĩa cú pháp nhất quán loại bỏ tính không liên quan để người ta có thể dễ dàng hiểu được mã sau khi họ quên nó. Copute và Haskell thi hành tính xác định của ngữ nghĩa hoạt động và khuyến khích Cameron không lặp lại chính mình (DRY), bởi vì chúng chỉ cho phép mô hình chức năng thuần túy.


2 Ngay cả khi chúng ta có thể chứng minh ngữ nghĩa của một chương trình, ví dụ với ngôn ngữ Coq, điều này bị giới hạn trong ngữ nghĩa được thể hiện trong cách gõ và việc gõ không bao giờ có thể nắm bắt được tất cả các ngữ nghĩa của chương trình, ngay cả đối với các ngôn ngữ chưa hoàn thành Turing, ví dụ với HTML + CSS, có thể biểu thị các kết hợp không nhất quán do đó có ngữ nghĩa không xác định.

3 Nhiều giải thích không chính xác cho rằng chỉ có lập trình mệnh lệnh mới có các câu lệnh được tổng hợp. Tôi đã làm rõ sự nhầm lẫn này giữa lập trình mệnh lệnh và chức năng . Ví dụ, thứ tự của các câu lệnh HTML không làm giảm tính nhất quán về nghĩa của chúng.


Chỉnh sửa: Tôi đã đăng bình luận sau đây lên blog của Robert Harper:

trong lập trình chức năng ... phạm vi biến thể của một biến là một loại

Tùy thuộc vào cách người ta phân biệt chức năng với lập trình mệnh lệnh, 'khả năng được gán' của bạn trong một chương trình bắt buộc cũng có thể có một loại đặt ràng buộc vào tính biến thiên của nó.

Định nghĩa không bị nhầm lẫn duy nhất mà tôi hiện đánh giá cao cho lập trình chức năng là a) các hàm như các đối tượng và loại hạng nhất, b) ưu tiên cho đệ quy trên các vòng lặp và / hoặc c) các hàm thuần túy tức là các hàm đó không ảnh hưởng đến ngữ nghĩa mong muốn của chương trình khi được ghi nhớ ( do đó lập trình chức năng hoàn toàn thuần túy không tồn tại trong ngữ nghĩa biểu thị mục đích chung do tác động của ngữ nghĩa hoạt động, ví dụ phân bổ bộ nhớ ).

Thuộc tính idempotent của một hàm thuần có nghĩa là hàm gọi trên các biến của nó có thể được thay thế bằng giá trị của nó, thường không phải là trường hợp cho các đối số của một thủ tục bắt buộc. Các hàm thuần túy dường như là wrt khai báo cho các chuyển trạng thái không tách rời giữa các kiểu đầu vào và kết quả.

Nhưng thành phần của các hàm thuần túy không duy trì bất kỳ tính nhất quán nào như vậy, bởi vì có thể mô hình hóa một quy trình bắt buộc tác dụng phụ (trạng thái toàn cầu) bằng ngôn ngữ lập trình hàm thuần túy, ví dụ IOMonad của Haskell và hơn nữa, hoàn toàn không thể ngăn chặn việc đó bất kỳ Turing hoàn thành ngôn ngữ lập trình chức năng thuần túy.

Như tôi đã viết vào năm 2012 có vẻ như có sự đồng thuận tương tự của các bình luận trong blog gần đây của bạn , rằng lập trình khai báo là một nỗ lực để nắm bắt khái niệm rằng ngữ nghĩa dự định không bao giờ mờ đục. Ví dụ về ngữ nghĩa mờ là sự phụ thuộc vào thứ tự, sự phụ thuộc vào việc xóa ngữ nghĩa cấp cao hơn ở lớp ngữ nghĩa hoạt động (ví dụ: phôi không phải là chuyển đổi và tổng quát giới hạn ngữ nghĩa cấp cao hơn ) và không thể kiểm tra được các giá trị biến đổi đúng) bởi ngôn ngữ lập trình.

Vì vậy, tôi đã kết luận rằng chỉ những ngôn ngữ hoàn chỉnh không Turing mới có thể được khai báo.

Do đó, một thuộc tính rõ ràng và khác biệt của ngôn ngữ khai báo có thể là đầu ra của nó có thể được chứng minh là tuân theo một số quy tắc tổng quát. Ví dụ, đối với bất kỳ chương trình HTML cụ thể nào (bỏ qua sự khác biệt trong cách phân tích trình thông dịch) không được viết theo kịch bản (nghĩa là không hoàn thành Turing) thì có thể đếm được biến thiên đầu ra của nó. Hay ngắn gọn hơn, một chương trình HTML là một chức năng thuần túy của tính biến đổi của nó. Ditto một chương trình bảng tính là một hàm thuần túy của các biến đầu vào của nó.

Vì vậy, dường như đối với tôi, các ngôn ngữ khai báo là phản đề của đệ quy không giới hạn , nghĩa là các định lý không hoàn chỉnh thứ hai của Gôdel không thể được chứng minh.

Lesie Lamport đã viết một câu chuyện cổ tích về cách Euclid có thể đã làm việc xung quanh các định lý không hoàn chỉnh của Gôdel áp dụng cho các bằng chứng toán học trong bối cảnh ngôn ngữ lập trình bằng cách kết hợp giữa các loại và logic (tương ứng Curry-Howard, v.v.).


Robert Harper dường như đồng ý với tôi về sự vô nghĩa của hầu hết các định nghĩa về khai báo , nhưng tôi không nghĩ rằng anh ta đã nhìn thấy tôi ở trên. Anh ấy tiến gần đến định nghĩa của tôi, nơi anh ấy thảo luận về ngữ nghĩa học biểu thị, nhưng anh ấy không hiểu định nghĩa của tôi. Các mô hình (ngữ nghĩa denotational) là cấp cao hơn .
Shelby Moore III

7

Lập trình bắt buộc: nói với máy máy trực tuyến, cách làm một cái gì đó và kết quả là những gì bạn muốn xảy ra sẽ xảy ra.

Lập trình khai báo: nói với máy trên máy tính mạng những gì bạn muốn xảy ra và để máy tính tìm ra cách thực hiện.

Ví dụ về mệnh lệnh

function makeWidget(options) {
    const element = document.createElement('div');
    element.style.backgroundColor = options.bgColor;
    element.style.width = options.width;
    element.style.height = options.height;
    element.textContent = options.txt;

    return element;
}

Ví dụ về khai báo

function makeWidget(type, txt) {
    return new Element(type, txt);
}

Lưu ý: Sự khác biệt không phải là một trong những điều ngắn gọn hay phức tạp hay trừu tượng. Như đã nêu, sự khác biệt là làm thế nào so với những gì .


2
tốt nhưng tốt hơn nếu bạn cung cấp ít nhất một ví dụ cho cả hai!
Pardeep Jain 6/2/2016

4

Ngày nay, trọng tâm mới: chúng ta cần phân loại cũ?

Các khía cạnh Bắt buộc / Tuyên bố / Chức năng trước đây rất tốt để phân loại các ngôn ngữ chung, nhưng hiện nay tất cả "ngôn ngữ lớn" (như Java, Python, Javascript, v.v.) đều có một số tùy chọn (điển hình là khung ) để diễn đạt với "trọng tâm khác" hơn cái chính của nó (mệnh lệnh thông thường) và để diễn tả các quá trình song song, các hàm khai báo, lambdas, v.v.

Vì vậy, một biến thể tốt của câu hỏi này là "khía cạnh nào là tốt để phân loại các khung ngày nay?" ... Một khía cạnh quan trọng là thứ mà chúng ta có thể gắn nhãn "phong cách lập trình" ...

Tập trung vào sự hợp nhất dữ liệu với thuật toán

Một ví dụ tốt để giải thích. Như bạn có thể đọc về jQuery tại Wikipedia ,

Tập hợp các tính năng cốt lõi của jQuery - lựa chọn, truyền tải và thao tác phần tử DOM -, được kích hoạt bởi công cụ chọn của nó (...), tạo ra một "kiểu lập trình" mới, hợp nhất các thuật toán và cấu trúc dữ liệu DOM

Vì vậy, jQuery là tốt nhất (phổ biến) ví dụ về tập trung vào một "phong cách lập trình mới" , có nghĩa là không chỉ định hướng đối tượng, là " keo thuật toán và dữ liệu cấu trúc ". jQuery có phần phản ứng như bảng tính, nhưng không phải là "hướng theo ô", là " định hướng nút DOM " ... So sánh các kiểu chính trong ngữ cảnh này:

  1. Không hợp nhất : trong tất cả các "ngôn ngữ lớn", trong bất kỳ biểu thức Chức năng / Tuyên bố / mệnh lệnh nào, thông thường là "không hợp nhất" dữ liệu và thuật toán, ngoại trừ theo một số hướng đối tượng, đó là sự hợp nhất trong quan điểm cấu trúc xoay vòng nghiêm ngặt .

  2. Một số hợp nhất : tất cả các chiến lược hợp nhất cổ điển, ngày nay có một số khung sử dụng nó như mô hình ... dataflow , lập trình hướng sự kiện (hoặc các ngôn ngữ cụ thể của miền cũ như awkXSLT ) ... Giống như lập trình với bảng tính hiện đại, chúng cũng ví dụ về phong cách lập trình phản ứng .

  3. Hợp nhất lớn : là "phong cách jQuery" ... jQuery là ngôn ngữ cụ thể của miền tập trung vào " thuật toán hợp nhấtcấu trúc dữ liệu DOM ".
    PS: các "ngôn ngữ truy vấn" khác, như XQuery, SQL (với PL là tùy chọn biểu thức mệnh lệnh) cũng là các ví dụ hợp nhất thuật toán dữ liệu, nhưng chúng là các đảo , không hợp nhất với các mô-đun hệ thống khác ... Mùa xuân , khi sử dụng find()-variants và Điều khoản đặc tả , là một ví dụ tổng hợp tốt khác.


3

Lập trình khai báo là lập trình bằng cách biểu thị một số logic vượt thời gian giữa đầu vào và đầu ra, ví dụ, trong mã giả, ví dụ sau đây sẽ là khai báo:

def factorial(n):
  if n < 2:
    return 1
  else:
    return factorial(n-1)

output = factorial(argvec[0])

Chúng tôi chỉ định nghĩa một mối quan hệ gọi là 'giai thừa' ở đây và xác định mối quan hệ giữa đầu ra và đầu vào là mối quan hệ đó. Như là điều hiển nhiên ở đây, về bất kỳ ngôn ngữ có cấu trúc nào cũng cho phép lập trình khai báo được mở rộng. Một ý tưởng trung tâm của lập trình khai báo là dữ liệu bất biến, nếu bạn gán cho một biến, bạn chỉ làm như vậy một lần, và sau đó không bao giờ lặp lại. Các định nghĩa khác, chặt chẽ hơn đòi hỏi rằng có thể không có tác dụng phụ nào cả, những ngôn ngữ này đôi khi được gọi là 'hoàn toàn khai báo'.

Kết quả tương tự trong một phong cách bắt buộc sẽ là:

a = 1
b = argvec[0]
while(b < 2):
  a * b--

output = a

Trong ví dụ này, chúng tôi thể hiện không có mối quan hệ logic tĩnh vượt thời gian giữa đầu vào và đầu ra, chúng tôi đã thay đổi địa chỉ bộ nhớ theo cách thủ công cho đến khi một trong số chúng giữ kết quả mong muốn. Rõ ràng là tất cả các ngôn ngữ cho phép một số ngữ nghĩa khai báo được mở rộng, nhưng không phải tất cả các ngôn ngữ cho phép bắt buộc, một số ngôn ngữ khai báo 'hoàn toàn' cho phép tác dụng phụ và đột biến hoàn toàn.

Các ngôn ngữ khai báo thường được cho là chỉ định 'những gì phải được thực hiện', trái ngược với 'cách thực hiện', tôi nghĩ đó là một cách viết sai, các chương trình khai báo vẫn chỉ định cách người ta phải nhận từ đầu vào đến đầu ra, nhưng theo một cách khác, mối quan hệ bạn chỉ định phải được tính toán hiệu quả (thuật ngữ quan trọng, hãy tìm kiếm nếu bạn không biết). Một cách tiếp cận khác là lập trình không điều kiện, thực sự chỉ xác định những điều kiện mà kết quả đáp ứng nhiều, trước khi việc triển khai của bạn chỉ làm cạn kiệt tất cả các đường dẫn về thử nghiệm và lỗi cho đến khi thành công.

Các ngôn ngữ khai báo thuần túy bao gồm Haskell và Pure Prolog. Một thang trượt từ cái này sang cái kia sẽ là: Pure Prolog, Haskell, OCaml, Scheme / Lisp, Python, Javascript, C--, Perl, PHP, C ++, Pascall, C, Fortran, hội


Bạn đã không xác định lập trình chức năng. Bạn ngụ ý không chính xác, "một số ngôn ngữ khai báo" hoàn toàn ", rằng lập trình khai báo có thể không trong sạch . Lập trình khai báo đòi hỏi sự bất biến của các giá trị được lưu trữ, lập trình mệnh lệnh thì không. Xem câu trả lời của tôi . Tính không thay đổi là chất lượng "vượt thời gian "-- thấy rằng khai báo của bạn factorialkhông làm thay đổi bất kỳ giá trị nào.
Shelby Moore III

3

Một số câu trả lời tốt ở đây liên quan đến "các loại" được ghi chú.

Tôi gửi một số khái niệm bổ sung, "kỳ lạ" hơn thường được liên kết với đám đông lập trình chức năng:

  • Ngôn ngữ cụ thể miền hoặc lập trình DSL : tạo một ngôn ngữ mới để xử lý vấn đề hiện tại.
  • Lập trình meta : khi chương trình của bạn viết các chương trình khác.
  • Lập trình tiến hóa : nơi bạn xây dựng một hệ thống liên tục cải thiện chính nó hoặc tạo ra các thế hệ chương trình con tốt hơn liên tiếp.

3

Tôi nghĩ rằng phân loại của bạn là không chính xác. Có hai loại mệnh lệnh ngược và khai báo. Chức năng chỉ là một kiểu con của khai báo. BTW, wikipedia nêu thực tế tương tự.


+1: Đúng, các mô hình là táo và cam.
Nikhil Chelliah

FP không "chỉ là một kiểu con của khai báo". FP là trực giao với sự phân cực của mệnh lệnh so với DP. DP yêu cầu sự bất biến của các giá trị được lưu trữ, không tinh khiết . Wikipedia đang kết hợp FP thuần túy với FP, với tuyên bố vô lý rằng các khái niệm sau đây "nói chung là xa lạ với lập trình mệnh lệnh": các hàm hạng nhất, đệ quy, thứ tự đánh giá và gõ tĩnh. Sau đó Wikipedia thừa nhận " không lập trình chức năng trong các ngôn ngữ phi chức năng".
Shelby Moore III

Wikipedia là sai về điểm này. Nhiều ngôn ngữ chức năng phổ biến cho phép lập trình theo "kiểu khai báo", nếu bạn chọn, nhưng không phải là ngôn ngữ khai báo. Nhưng điều tương tự cũng có thể nói về C, nơi bạn vẫn có thể lập trình theo kiểu chức năng nếu bạn chọn, sử dụng void * s.
Plynx

Có lẽ, tôi nên đã rõ ràng hơn về điểm này, nhưng từ phía bên kia, tôi sẽ không gây rối với chủ đề bắt đầu với các chi tiết không liên quan (imo). Tôi thấy rằng các ngôn ngữ chức năng có xu hướng được sử dụng khai báo. Bạn có thể cố gắng viết khai báo và / hoặc chức năng trong ASM hoặc C hoặc bạn có thể có thể viết chương trình bắt buộc trong Lisp nhưng tôi nghi ngờ rằng nó sẽ rất hữu ích hoặc cung cấp thông tin cho tác giả câu hỏi. Vì vậy, về bản chất tôi vẫn xem câu trả lời của mình là phù hợp, ngay cả khi nó có thể được diễn đạt khác nhau.
Rorick

2

Tóm lại, một phong cách lập trình càng nhấn mạnh những gì (phải làm) trừu tượng hóa các chi tiết của Làm thế nào (để làm điều đó) thì phong cách đó càng được coi là khai báo. Điều ngược lại là đúng đối với mệnh lệnh. Lập trình chức năng được liên kết với phong cách khai báo.


Xem ý kiến ​​của tôi dưới đây các câu trả lời khác. FP không phải lúc nào cũng khai báo. Điều gì so với cách phân loại sai cho IP so với DP, vì cả DP và IP đều có logic liên quan đến cái gì và như thế nào.
Shelby Moore III
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.