2011-07-13 43 views
8

Tôi mới bắt đầu F # vì vậy hãy tử tế nếu điều này là cơ bản.F # Lazy Evaluation vs Non-Lazy

Tôi đã đọc một chức năng được đánh dấu là lười biếng chỉ được đánh giá một lần và sau đó được lưu vào bộ nhớ cache. Ví dụ:

let lazyFunc = lazy (1 + 1) 
let theValue = Lazy.force lazyFunc 

So với phiên bản này mà thực sự sẽ chạy mỗi khi nó được gọi là:

let eagerFunc = (1 + 1) 
let theValue = eagerFunc 

Trên cơ sở đó, nên tất cả chức năng được thực hiện lười biếng? Khi nào bạn không muốn? Điều này đến từ tài liệu trong sách "Bắt đầu từ F #".

+0

Phiên bản F # này là gì? Tôi có một chuỗi hành động uể oải, nhưng không được tạo ra một cách công khai theo cách đó. Tôi đang cố ép nó hoàn thành. – octopusgrabbus

Trả lời

13

Trước hết, nó có thể hữu ích cần lưu ý rằng không ai trong số những điều bạn đã xác định là một chức năng - eagerFunctheValue là những giá trị của loại intlazyFunc là một giá trị kiểu Lazy<int>. Với

let lazyTwo = lazy (1 + 1) 

let eagerTwo = 1 + 1 

biểu thức 1 + 1 sẽ không được đánh giá nhiều hơn một lần dù có bao nhiêu lần bạn sử dụng eagerTwo. Sự khác biệt là 1 + 1 sẽ được đánh giá chính xác một lần khi địnheagerTwo, nhưng sẽ được đánh giá tại hầu hết các một lần khi lazyTwosử dụng (nó sẽ được đánh giá lần đầu tiên rằng Value tài sản được truy cập, và sau đó được lưu vào bộ nhớ cache để sử dụng thêm Value không cần phải tính toán lại nó). Nếu lazyTwo 's Value không bao giờ được truy cập, thì nội dung của nó 1 + 1 sẽ không bao giờ được đánh giá.

Thông thường, bạn sẽ không thấy nhiều lợi ích khi sử dụng các giá trị lười biếng bằng ngôn ngữ nghiêm ngặt như F #. Họ thêm một lượng nhỏ chi phí từ khi truy cập thuộc tính Value yêu cầu kiểm tra xem giá trị đã được tính chưa. Họ có thể giúp bạn tiết kiệm một chút tính toán nếu bạn có một cái gì đó như let lazyValue = lazy someVeryExpensiveCalculationThatMightNotBeNeeded(), vì việc tính toán tốn kém sẽ chỉ diễn ra nếu giá trị thực sự được sử dụng. Họ cũng có thể làm cho một số thuật toán chấm dứt mà nếu không sẽ không, nhưng điều này không phải là một vấn đề lớn trong F #. Ví dụ:

// throws an exception if x = 0.0 
let eagerDivision x = 
    let oneOverX = 1.0/x 
    if x = 0.0 then 
     printfn "Tried to divide by zero" // too late, this line is never reached 
    else 
     printfn "One over x is: %f" oneOverX 

// succeeds even if x = 0.0, since the quotient is lazily evaluated 
let lazyDivision x = 
    let oneOverX = lazy (1.0/x) 
    if x = 0.0 then 
     printfn "Tried to divide by zero" 
    else 
     printfn "One over x is: %f" oneOverX.Value 
+0

Không sử dụng '()' ngụ ý rằng nó là một hàm? Điểm tốt trong mọi trường hợp. Bạn đang đúng ví dụ của tôi là siêu tầm thường. – Yuck

+0

@Yuck - dấu ngoặc đơn có nghĩa là những thứ khác nhau trong các ngữ cảnh khác nhau. Khi bạn sử dụng chúng như '(1 + 1)' thì chúng chỉ đơn thuần là phục vụ để chỉ nhóm và ưu tiên. Nếu bạn đã định nghĩa 'let eagerFunc() = ...' thì các dấu ngoặc đơn chỉ ra rằng 'eagerFunc' là một hàm, nhưng lưu ý rằng điều này khác với những gì bạn đã viết. – kvb

6

Nếu thực hiện chức năng có tác dụng phụ và điều quan trọng là phải xem các tác dụng phụ mỗi khi hàm được gọi (nói nó kết thúc tốt chức năng I/O), bạn sẽ không muốn nó bị lười.

Ngoài ra còn có chức năng mà rất tầm thường mà thực hiện chúng mỗi lần là nhanh hơn so với bộ nhớ đệm value--

5

let eagerFunc = (1 + 1) là một phép ràng buộc, và sẽ chỉ thực hiện một lần. let eagerFunc() = (1 + 1) là một chức năng chấp nhận unit (không có gì) và trả lại int. Nó sẽ thực hiện mỗi khi nó được gọi. Trong một nghĩa nào đó, mọi chức năng đều lười biếng, nghĩa là, nó chỉ thực hiện khi được gọi. Tuy nhiên, từ khóa lazy (và System.Lazy, mà nó trả về) sẽ thực thi biểu thức/hàm được gán cho nó nhiều nhất một lần. Các cuộc gọi tiếp theo đến thuộc tính Value sẽ trả về kết quả được lưu trong bộ nhớ cache. Điều này rất hữu ích khi tính toán giá trị là tốn kém.

Nhiều chức năng sẽ không phù hợp để sử dụng với lazy vì chúng không xác định (có thể trả lại kết quả khác nhau với mỗi lệnh gọi) hoặc tham số hóa. Tất nhiên, có thể sử dụng một phiên bản được áp dụng đầy đủ (một giá trị được cung cấp cho mỗi tham số) của các hàm như vậy, nhưng nói chung sự biến đổi là mong muốn.