Lập trình khai báo python

Vì vậy, hãy thảo luận về sự khác biệt trong cách bạn tương tác với Alice và Bob. Chúng tôi bắt đầu bằng cách nhớ lại nhiệm vụ thực tế là gì. Nhiệm vụ của bạn là tổ chức một bữa tối tân gia với bạn bè của mình. Do đó, mức độ trừu tượng của bạn cho nhiệm vụ cụ thể đó phải ở cấp độ tổ chức. Bằng cách gửi tin nhắn mời tới Alice, bạn đã tuyên bố rằng bạn muốn cô ấy có mặt tại chỗ của bạn vào một thời điểm nhất định mà không quan tâm đến việc cô ấy sẽ thực sự hoàn thành việc này như thế nào. Với Bob, đó là một câu chuyện hoàn toàn khác. Bạn không chỉ tuyên bố ý định của mình mà còn cung cấp rất nhiều hướng dẫn về cách thực hiện những điều này. Bằng cách đó, bạn rời khỏi cấp độ tổ chức một bữa ăn tối và chuyển sang nhiệm vụ lên kế hoạch cho một chuyến đi và đây thậm chí không phải là phần tồi tệ nhất. Bạn cũng đưa ra nhiều giả định dễ mắc sai lầm, chẳng hạn như Bob sẽ đi tàu để đến thành phố của bạn chứ không phải bởi một cộng đồng đi chung xe trực tuyến nào đó. Điều này có thể rẻ hơn và nhanh hơn rất nhiều

Tư duy khai báo

Nếu bây giờ chúng ta chuyển ví dụ này sang lập trình, chúng ta sẽ mô tả cách tương tác với Alice là mệnh lệnh và tương tác với Bob là mệnh lệnh. Tóm lại, lập trình khai báo là viết mã theo cách mô tả những gì bạn muốn làm chứ không phải cách bạn muốn làm. Nghe có vẻ đơn giản, phải không? . Có phải bảo ai đó đi một chuyến tàu cụ thể nào đó luôn luôn nói với ai đó cách làm điều gì đó và do đó là bắt buộc?

Giả sử chúng ta muốn thực hiện một số phép toán đại số tuyến tính, cụ thể là chúng ta muốn tính tổng hai vectơ n chiều \[a\] . Vì chúng ta đang ở trong miền đại số tuyến tính bằng cách sử dụng một số hệ thống đại số máy tính [ \[b\]. Since we are in the domain of linear algebra using some computer algebra system [CAS ], nên chúng ta có thể chỉ cần khai báo những gì chúng ta muốn \[c = a + b\]. Calculating \[c\] với sự trợ giúp của một vòng lặp rõ ràng là bắt buộc trong ngữ cảnh nhất định. Nhược điểm của việc sử dụng vòng lặp cho việc này rất đa dạng. Đầu tiên, chúng tôi sẽ xác định một thứ tự ẩn của các phần tử sẽ tổng hợp trước. Điều này loại bỏ khả năng phần mềm CAS của chúng tôi chọn một SIMD CPU gốc . Thứ hai, mã của chúng tôi trở nên khó đọc hơn nhiều và chúng tôi đang vi phạm mức độ trừu tượng đơn lẻ cũng như nguyên tắc phân tách mối quan tâm, cả hai đều có mối liên hệ mật thiết với lập trình khai báo và tư duy. Mặt khác, nếu nhiệm vụ của chúng ta là giải quyết một vấn đề tối ưu hóa tuyến tính nhất định, thì việc bắt đầu tự triển khai thuật toán Đơn hình với sự trợ giúp của các phép toán vectơ để giải quyết vấn đề đó cũng có thể được coi là bắt buộc. operation due to our over-specification of how to do it. Secondly, our code becomes much less readable and we are violating the single-level of abstraction as well as the separation of concerns principle which are both highly connected to declarative programming and thinking. If, on the other hand though, our task is to solve a given linear optimization problem, starting to implement a Simplex algorithm on our own with the help of vector operations in order to solve it can also be considered imperative.

Đã nhận ra tầm quan trọng của mức độ trừu tượng hóa. miền mà nhiệm vụ của chúng ta tồn tại, câu hỏi rõ ràng là câu hỏi sau. Làm thế nào để các ngôn ngữ như SQL và Prolog hoặc Datalog phù hợp với bức tranh của chúng ta vì chúng luôn được tuyên bố là có tính khai báo? . Do đó, họ chỉ tập trung vào giải quyết các vấn đề trong một lĩnh vực duy nhất. Đối với SQL miền đó đang truy vấn cơ sở dữ liệu quan hệ. Để hoàn thành nhiệm vụ này, khái niệm toán học về đại số quan hệ đã được thiết lập vay mượn từ lý thuyết tập hợp. Bằng cách sử dụng đại số quan hệ làm hộp công cụ để xác định các truy vấn, chúng tôi có một lớp trừu tượng ở mức cao, phù hợp với nhiệm vụ hiện tại nhưng không phù hợp với bất kỳ nhiệm vụ nào khác bên ngoài miền đó. Quan sát tương tự đối với Prolog và Datalog áp dụng một tập hợp các khái niệm toán học, nổi bật nhất là mệnh đề Horn, để giải quyết các vấn đề trong lĩnh vực lập trình hậu cần. Tại thời điểm đó, cũng cần lưu ý rằng lập trình chức năng cũng có thể được coi là lập trình khai báo. Một lần nữa, lớp trừu tượng dựa trên các khái niệm toán học với một số hoạt động khả thi nhất định nhưng cũng có những hạn chế. Ví dụ: một hàm thường được coi là bất kỳ giá trị nào khác và do đó có thể được xâu chuỗi với các hàm khác, được truyền dưới dạng tham số hoặc thậm chí được trả về từ một hàm khác. So với ngôn ngữ mệnh lệnh, hạn chế nổi tiếng nhất là các chức năng không được phép có bất kỳ tác dụng phụ nào.

Trước khi chúng ta bắt đầu áp dụng các khái niệm khai báo, một lời cảnh báo về khái niệm trừu tượng nói chung. Trong một thế giới hoàn hảo, một lớp trừu tượng sẽ che giấu hoàn toàn các hoạt động bên trong bên dưới nó với người dùng. Nhưng ngay cả DSL như SQL được xây dựng trên cơ sở lý thuyết được hình thành rõ ràng như vậy . Điều này có thể được trải nghiệm khá dễ dàng khi xem xét hai truy vấn khác nhau nhưng tương đương về mặt logic khác nhau về hiệu suất theo thứ tự độ lớn. Luật trừu tượng rò rỉ của Spolsky thậm chí còn tuyên bố rằng “Tất cả các trừu tượng không tầm thường, ở một mức độ nào đó, đều bị rò rỉ. ” Bên cạnh cảnh báo này, trừu tượng vẫn là một công cụ mạnh mẽ để xử lý sự phức tạp.

Lập trình khai báo

Sau khi đã thiết lập một số hiểu biết về lập trình khai báo và ý tưởng chung đằng sau nó, trọng tâm bây giờ là áp dụng điều này vào lập trình với Python. Chúng tôi bắt đầu với một số ví dụ đơn giản. Hãy tưởng tượng chúng ta muốn danh sách các số bình phương từ 1 đến 10. Cách tiếp cận rõ ràng sẽ là

result = []
for i in range[1, 11]:
    result.append[i**2]

Đây chắc chắn là một cách bắt buộc để giải quyết vấn đề. Chúng tôi đã chỉ định quá mức vấn đề theo nghĩa là chúng tôi đưa ra một thứ tự về cách tính toán giải pháp. Về cơ bản, chúng tôi nói trong vài dòng đó. “Đầu tiên là ô vuông 1 và nối kết quả vào danh sách, sau đó là ô vuông 2, v.v.”. Bằng cách áp dụng tính năng hiểu danh sách của Python, chúng tôi tiến gần hơn đến câu hỏi thực tế của mình

result = [i**2 for i in range[1, 11]]

Bây giờ chúng tôi chưa chỉ định một thứ tự mà về mặt lý thuyết thậm chí sẽ cho phép trình thông dịch Python tính toán kết quả song song. Bên cạnh khả năng hiểu danh sách, cú pháp tương tự cũng tồn tại đối với từ điển và thậm chí bộ

Nói về tập hợp, loại tập hợp thậm chí có thể là loại dữ liệu được đánh giá thấp nhất trong thư viện chuẩn của Python. Hình ảnh bạn muốn kiểm tra xem một số câu trong bài báo mới xuất bản có khớp chính xác với câu trong bài báo của bạn hay không để chứng minh đạo văn. Tất nhiên, có một công cụ đặc biệt cho việc đó nhưng giả sử bạn chỉ có Python. Cách tiếp cận ngây thơ sẽ là lấy một câu từ bài báo đầu tiên và so sánh nó với tất cả các câu của bài báo kia. Vì thuật toán này sẽ phức tạp \[O[n^2]\] nên hiệu suất sẽ khá tệ nếu bạn muốn kiểm tra hai bài báo mở rộng. Vì vậy, làm thế nào một người sẽ áp dụng tư duy khai báo ở đây? . e. chúng tôi không quan tâm đến thứ tự câu cũng như thực tế là có thể có câu trùng lặp. Do đó, chúng ta có thể áp dụng lý thuyết tập hợp dưới dạng lớp trừu tượng và coi bài báo của chúng ta là tập hợp \[A\] câu và câu kia là tập hợp \[B\]. By doing so we are able to now express what we want in a single declaration \[A\cap B\] hoặc tương đương trong Python.

result = A & B

Một lần nữa, chúng tôi không chỉ đạt được khả năng đọc so với phiên bản có hai vòng lặp lồng nhau mà tôi đã bỏ qua ở đây mà việc thực thi cũng sẽ nhanh hơn nhiều do các thuật toán chuyên biệt dựa trên bảng băm được áp dụng bên dưới phần trừu tượng. Nói chung, vì Python cung cấp rất nhiều kiểu dữ liệu trừu tượng tích hợp sẵn, nên một lời khuyên hữu ích là bạn nên nghiên cứu chúng kỹ lưỡng để hiểu đầy đủ khả năng của chúng. Chẳng hạn, một từ điển có vẻ là một cái gì đó khá đơn giản nhưng nhận ra rằng một từ điển thực sự là một ánh xạ toán học cho phép chúng ta viết một bộ điều phối tao nhã chẳng hạn. Trước tiên, hãy giả sử một phiên bản bắt buộc

def dispatch[arg, value]:
    if arg == 'optionA':
        function_a[value]
    elif arg == 'optionB':
        function_b[value]
    elif arg == 'optionC':
        function_c[value]
    else:
        default[value]

Điều chúng tôi thực sự muốn nói là mỗi đối số ánh xạ tới một hàm mà sau đó được gọi với một giá trị nhất định như

dispatch = {'optionA': function_a,
            'optionB': function_b,
            'optionC': function_c}
dispatch.get[arg, default][value]

Một vấn đề thường gặp khác đã ăn sâu vào Python là cấu hình thông qua mô-đun Python. Các thư viện như Sphinx, các công cụ thiết lập của Python và nhiều thư viện khác sử dụng các mô-đun Python thực tế để định cấu hình các cài đặt nhất định. Mặc dù điều này cho phép sự linh hoạt tối đa, nhưng các tệp cấu hình của chúng thường khó đọc và dễ bị lỗi. Một cách tiếp cận khai báo cho cấu hình là sử dụng ngôn ngữ đánh dấu chẳng hạn như YAML . Sử dụng các tệp YAML để định cấu hình chương trình Python có một số lợi thế. Thứ nhất, bất kỳ trình soạn thảo tốt nào cũng có thể phân tích cú pháp và do đó sẽ cảnh báo bạn về các lỗi cú pháp. Thứ hai, định dạng ánh xạ tới kiểu dữ liệu từ điển của Python và do đó, nhiều thư viện khác [e. g. thư viện xác thực dữ liệu như Voluptuous] hoạt động trên từ điển có thể dễ dàng áp dụng. C và C++ có một lịch sử lâu dài về tự động hóa xây dựng khai báo với sự trợ giúp của

result = [i**2 for i in range[1, 11]]
2 và khai báo của nó
result = [i**2 for i in range[1, 11]]
3. Ngoài ra, hàng hóa công cụ xây dựng và đóng gói của Rust đang áp dụng ngôn ngữ đánh dấu khai báo, cụ thể là TOML . Thông điệp mang đi rất rõ ràng và đơn giản. Ưu tiên ngôn ngữ đánh dấu hơn mô-đun Python để định cấu hình trong trường hợp bạn không cần thêm tính linh hoạt của toàn bộ ngôn ngữ lập trình.

Khi nói đến lập trình song song trong Python, lập trình khai báo cũng có thể hữu ích. Một lần nữa, mọi thứ ổn định và thất bại với trường hợp sử dụng thực tế, nhưng giả sử rằng chúng ta có một số tác vụ ở dạng hàm thuần túy, tôi. e. chức năng mà không có bất kỳ tác dụng phụ. Hơn nữa, một số tác vụ phụ thuộc vào kết quả của những tác vụ khác trong khi những tác vụ khác có thể được thực hiện song song. Nhất thiết chúng ta có thể sử dụng mô-đun

result = [i**2 for i in range[1, 11]]
4 của Python để chạy song song một số tác vụ nhất định, đồng bộ hóa khi cần thiết và đảm bảo rằng chúng ta không bị nhầm lẫn trong sổ sách kế toán. Suy nghĩ về vấn đề hiện tại, một lập trình viên khai báo sẽ nhận ra rằng một đồ thị tuần hoàn có hướng [ DAG ] cùng với một số khái niệm toán học như thứ tự tô pô sẽ tạo thành một biểu đồ phù hợp. . Sự hiển linh này sẽ dẫn anh ta trực tiếp đến một công cụ hay gọi là Dask cho phép xác định và chạy DAG theo cách khai báo. Ngoài ra, TensorFlow tuân theo một cách tiếp cận tương tự để cung cấp phương tiện cho đại số tuyến tính, song song, chủ yếu được sử dụng cho học sâu. Đồng thời các phiên bản mới hơn cũng cung cấp một lớp trừu tượng khác cho phép khai báo các lớp mạng thần kinh giống như Keras. Ở đây chúng ta có thể thấy rằng các gói phần mềm lớn hơn thậm chí còn cung cấp một số lớp trừu tượng được xây dựng dựa trên nhau và cho phép người dùng quyết định lớp trừu tượng nào phù hợp với nhiệm vụ hiện tại. Một ví dụ khác cho điều này là SQLAlchemy với lớp lõi và ORM  của nó.

Tại thời điểm này, bạn chắc chắn đã hiểu rõ về nó. Bản chất của lập trình khai báo là mô tả một vấn đề trong miền của nó, áp dụng các khái niệm cấp cao, do đó tập trung nhiều hơn vào cái gì và ít hơn vào cách thức. Điều này cho phép chúng tôi tăng khả năng đọc mã của mình, thường xuyên giảm số lượng lỗi lập trình và cũng tăng hiệu suất ít nhất so với triển khai ngây thơ. Để kết thúc bài đăng này, chúng ta hãy xem một ví dụ thú vị hơn từ lĩnh vực logic. Chúng tôi muốn áp dụng lập trình khai báo để giải một trong những câu đố Logelei của tờ báo Đức nổi tiếng Die Zeit


nằm ngang
A. tổng chữ số của hàng ngang C, C. số nguyên tố, E. màu nhạt, G. bội số lùi của hàng ngang A, H. tất cả các chữ số đều bằng nhau, tôi. dọc F lần dọc K, L. bội số của M, N dọc. bội số của phương ngang Q, P. dọc B là bội số, Q. số vuông, R. số vuông, S. số nguyên tố

thẳng đứng
Tất cả các số đều là số vuông

Có thể giải quyết vấn đề như vậy bằng các công cụ và thư viện phổ biến của Python nhưng cũng khá cồng kềnh. Vì chúng tôi biết rằng vấn đề này là một vấn đề từ lĩnh vực logic hình thức, nên thực sự không có lý do gì để rời khỏi lớp trừu tượng này. Với ý nghĩ đó, câu đố chỉ có thể được coi là một tập hợp các quy tắc và sự kiện. Chẳng hạn, chúng ta biết một thực tế là một số bao gồm các chữ số từ 0 đến 9 với chữ số đầu tiên là từ 1 đến 9. Một ví dụ cho quy tắc là một số nguyên \[n\] là số chính phương khi và chỉ khi tồn tại một số nguyên \[k\] so that \[k^2=n\]. We will use the Python library PyDatalog to translate the riddle into a proper form so that, after having stated all facts and rules, we can just ask for the values of each field of the table and PyDatalog will do the inference based on the knowledge we have given. The syntax of PyDatalog might seem a bit strange at first but it is really concise and powerful. The rule for a square number is stated as:

________số 8_______

Tốt nhất là đọc

result = [i**2 for i in range[1, 11]]
5 ngoài cùng bên trái như thể. Vì vậy, dòng trên nói rằng một số \[X\] là bình phương nếu \[\sqrt{X} . Theo cách tương tự, chúng ta có thể xác định quy tắc nếu một số chia hết cho số khác với. . In an analogous manner, we can define a rule if one number is divisible by another with:

divisible[X, Y] 

Chủ Đề