Đánh giá lười biếng là một chiến lược đánh giá giữ đánh giá một biểu thức cho đến khi cần giá trị của nó. Nó tránh đánh giá lặp đi lặp lại. Haskell là một ví dụ điển hình về ngôn ngữ lập trình chức năng mà các nguyên tắc cơ bản của nó dựa trên Đánh giá lười biếng
Đánh giá lười biếng được sử dụng trong các chức năng bản đồ Unix để cải thiện hiệu suất của chúng bằng cách chỉ tải các trang được yêu cầu từ đĩa. Sẽ không có bộ nhớ nào được phân bổ cho các trang còn lại
Đánh giá lười biếng - Ưu điểm
Nó cho phép bộ thực thi ngôn ngữ loại bỏ các biểu thức con không được liên kết trực tiếp với kết quả cuối cùng của biểu thức
Nó làm giảm độ phức tạp về thời gian của một thuật toán bằng cách loại bỏ các phép tính tạm thời và các điều kiện
Nó cho phép lập trình viên truy cập các thành phần của cấu trúc dữ liệu không theo thứ tự sau khi khởi tạo chúng, miễn là chúng không có bất kỳ phụ thuộc vòng tròn nào
Nó phù hợp nhất để tải dữ liệu sẽ được truy cập không thường xuyên
Đánh giá lười biếng - Nhược điểm
Nó buộc thời gian chạy ngôn ngữ giữ việc đánh giá các biểu thức con cho đến khi nó được yêu cầu trong kết quả cuối cùng bằng cách tạo thunks [đối tượng bị trì hoãn]
Đôi khi nó làm tăng độ phức tạp không gian của một thuật toán
Rất khó để tìm thấy hiệu suất của nó vì nó chứa rất nhiều biểu thức trước khi thực hiện
Đánh giá lười biếng bằng Python
Phương thức phạm vi trong Python tuân theo khái niệm Đánh giá lười biếng. Nó tiết kiệm thời gian thực hiện cho các phạm vi lớn hơn và chúng tôi không bao giờ yêu cầu tất cả các giá trị cùng một lúc, vì vậy nó cũng tiết kiệm mức tiêu thụ bộ nhớ. Hãy xem ví dụ sau
Sự lười biếng trong các ngôn ngữ lập trình là điều gì đó đã xảy ra khá nhiều trong thời gian gần đây. Chắc chắn có vẻ như rất nhiều người muốn nó được triển khai bằng ngôn ngữ yêu thích của họ. Điều này thường xảy ra sau khi đã sử dụng nó trong Haskell, nơi nó cực kỳ hữu ích, dễ hiểu và dường như không có bất kỳ cạm bẫy nghiêm trọng nào
Tôi đã chơi xung quanh với các công cụ trang trí trong Python và sự lười biếng dường như là thứ sẽ tạo nên một thử nghiệm tốt. Khái niệm đánh giá lười biếng thực sự không khó lắm; . Thấy rằng python coi các hàm là cấu trúc bậc cao, tôi. e, không khó để triển khai chức năng cho một hàm trả về một hàm, sẵn sàng để được đánh giá khi cần
Nhưng điều này là không thực sự đủ. Tôi muốn một cách để sự lười biếng thực sự minh bạch, tốt nhất là chỉ sử dụng một công cụ trang trí và đó là tất cả. Đối với điều này, giải pháp không rõ ràng ngay lập tức, nhưng python là một ngôn ngữ linh hoạt, vì vậy tôi nghĩ có thể có cơ hội. Với tất cả những điều này trong đầu, tôi đã suy nghĩ rất lâu về việc đánh giá lười biếng trong python và cuối cùng đã quyết định một vài điều
- Sẽ không thể thêm sự lười biếng trong suốt mà không thao tác rộng rãi mã byte hoặc phần bên trong nghiêm trọng của trình biên dịch. Cả hai điều tôi không muốn làm
- Đánh giá lười biếng minh bạch không thực sự là một ý tưởng hay trong một ngôn ngữ mệnh lệnh
Tôi sẽ bắt đầu với điểm đầu tiên. Hãy xem xét một số ví dụ, bắt đầu với trang trí lười biếng của tôi
def lazy[func]: def lazyfunc[*args, **kwargs]: wrapped = lambda x : func[*args, **kwargs] wrapped.__name__ = "lazy-" + func.__name__ return wrapped return lazyfunc
Đây là một điều khá đơn giản - cách điển hình để mô phỏng đánh giá lười biếng. Đưa ra một chức năng, nó trả về một chức năng mới, khi được gọi, đánh giá chức năng được truyền ban đầu
Chúng ta có thể sử dụng điều này như một trang trí trong những điều sau đây
def hello_add_normal[x, y]: print "Hello I am Normal!" return x + y @lazy def hello_add_lazy[x, y]: print "Hello I am Lazy!" return x + y
Thực thi chức năng đầu tiên sẽ đánh giá nó, nhưng chức năng thứ hai thực hiện một cuộc gọi bổ sung, được sử dụng khi chúng tôi yêu cầu giá trị thực của đánh giá. Bằng cách này, chúng ta có thể đánh giá kết quả khi chúng ta muốn [hoặc khi cần thiết, trong trường hợp lười biếng]
$ hello_add_normal[1, 2] Hello I am Normal! 3 $ hello_add_lazy[1, 2] $ myval = hello_add_lazy[1, 2] $ myval[] Hello I am Lazy! 3
Tại thời điểm này, lập trình viên người dùng hoàn toàn có thể thêm lệnh gọi bổ sung này [hoặc hàm "bắt buộc"] bất cứ khi nào họ muốn đánh giá, nhưng tôi đang tìm cách nào đó để python biết khi nào cần và thực hiện
Vì vậy, mẹo thực sự ở đây là thêm lệnh gọi bổ sung này vào bất kỳ thời điểm nào khi giá trị của hàm này được yêu cầu. Thật không may, điều này yêu cầu phân tích mã. Điều gì đó có thể xảy ra trong thời gian chạy python, nhưng chỉ ở cấp mã byte
Tuy nhiên, có lẽ điều này là đủ, tất cả đều nhân danh sự lười biếng đánh giá. ? . Chúng tôi muốn thêm một cuộc gọi bổ sung trước khi chuyển nhượng cho myval - sau khi chúng tôi vừa nhìn vào một biểu tượng mà chúng tôi biết là lười biếng. Vì mã python dựa trên ngăn xếp, nên có vẻ như bạn chỉ cần thêm một mã byte "gọi" bổ sung, sau khi xuất hiện bất kỳ biểu tượng "lười biếng" nào ở đầu ngăn xếp. Tuy nhiên, trong thực tế, mọi thứ trở nên khó khăn hơn một chút. Thật khó để theo dõi những gì ở cấp cao nhất của ngăn xếp và có nhiều sự phức tạp khác. Hãy xem ví dụ này
mylist = [hello_add_lazy[1,2], hello_add_lazy[2,5], hello_add_lazy[6,1]] hello = mylist[0]
Tại thời điểm này, nơi duy nhất chúng tôi đã tham chiếu đến biểu tượng "lười biếng" trong mã byte là trên cấu trúc danh sách, không có nơi nào gần nơi chúng tôi thực sự muốn thực hiện đánh giá tự động. Vì vậy, trong trường hợp này, chúng tôi phải theo dõi "danh sách những điều lười biếng" này và xem xét cả điều đó.
Chắc chắn, ví dụ này vẫn có thể giải quyết và suy luận, nhưng với các cấu trúc dữ liệu phức tạp tùy ý, các hàm xây dựng, hàm bậc cao và nhiều cấu trúc python phức tạp khác, tôi cảm thấy nó không đáng
Mã byte bị loại bỏ quá xa khỏi mô hình ngữ nghĩa của ngôn ngữ đối với loại phân tích này. Nếu bạn muốn đánh giá lười biếng một cách nghiêm túc thì nó phải được thực hiện trong trình biên dịch, điều này đưa tôi đến điểm thứ hai. Có lẽ chúng tôi thậm chí không muốn đánh giá lười biếng tự động
Python có hai biểu thức ngữ nghĩa có lẽ là mạnh nhất trong ngôn ngữ, đó là gán "=" và gọi "[]". Không cần phải nói, lộn xộn với những thứ này có lẽ không phải là ý tưởng tốt nhất. Thật không may đánh giá lười biếng làm cả hai. Hãy xem một ví dụ khác
def sleepy[x]: time.sleep[1] return x $ my_other_list = [sleepy[1], sleepy[2], sleepy[3]] [1,2,3]
Khi chạy, mã này ngủ trong ba giây rồi trả về danh sách [1,2,3]. Điều này không hề lười biếng chút nào, nhưng bạn có muốn nó làm bất cứ điều gì khác không? . Dấu ngoặc sau từ "sleepy" có nghĩa là hàm đang được gọi, bạn muốn nó được đánh giá. Đánh giá lười biếng gây rối với khái niệm này
Bài tập cũng bị nhầm lẫn, hãy xem một ví dụ khác
the_time = time.time[] time.sleep[1] next_time = time.time[] if the_time == next_time: print "BOOM"
Trong ví dụ này, tôi sẽ không muốn mã in ra "BOOM". Với việc đánh giá càng lười biếng càng tốt, nó sẽ. Tốt thôi, bạn nói, nhưng điều gì sẽ xảy ra nếu chúng ta đánh giá việc gán cho biến thay vào đó, để giữ đúng thứ tự. Chà, điều này giống như muốn kết hợp giữa thuần túy và không thuần túy, và cuối cùng, theo đối số này có nghĩa là cuối cùng bạn phải đánh giá việc gán cho BẤT KỲ cấu trúc dữ liệu nào, không chỉ các biến và điều này không hề lười biếng . Chuyển nhượng không có nghĩa là tương đương và chuyển nhượng cùng một lúc
Trong một ngôn ngữ như Haskell, loại mã này sẽ không thể thực hiện được nếu không có các đơn nguyên. Điều này là do trong Haskell không có tác dụng phụ trong các chức năng, không có cảm giác về thứ này thì thứ khác. Ngủ, in, tất cả những thứ đó sẽ không thể khai báo trong một chức năng và do đó, với việc đánh giá lười biếng, đó không phải là vấn đề. Không nhầm lẫn khi sự việc xảy ra
Đây không phải là trường hợp trong python. Lập trình không có tác dụng phụ rất khó và không phải lúc nào cũng là phương pháp ưa thích [ít nhất là đối với tôi]. Không có gì sai với các tác dụng phụ miễn là bạn biết nhiệm vụ và chức năng gọi thực sự có ý nghĩa gì về mặt ngữ nghĩa. Và dù sao đi nữa, chẳng phải việc biết khi nào các chức năng của bạn đang được gọi sẽ rõ ràng và dễ chịu hơn nhiều sao?
Đánh giá lười biếng là điều tuyệt vời trong Haskell, nhưng nó không phải là loại "tính năng ngôn ngữ" có thể và nên được thêm vào các ngôn ngữ theo ý thích. Hầu hết các hành vi đánh giá lười biếng có thể được mô phỏng thông qua các phương tiện khác, mà tôi nghĩ là cần thiết trong sự rõ ràng của chúng khi không xử lý một ngôn ngữ thuần túy, không có tác dụng phụ, chẳng hạn như Haskell