【问题标题】:Will the use of lazy in an F# property prevent the code from being evaluated when it shouldn't?在 F# 属性中使用惰性是否会阻止代码在不应该被评估时被评估?
【发布时间】:2021-10-19 12:25:57
【问题描述】:

我有一个共同的模式:

type something =
    {
        ... a lot of stuff
    }

    member this.Description =
        "a string description of the content, can be long tables, etc"

我希望仅在需要时评估 Description 属性;在许多情况下,它不会被使用,但它可以(主要是根据用户的请求)。

我注意到,即使不需要,此代码也会导致对 Description 进行评估。所以我把代码移到了一个函数中:Describe() 就解决了问题。

在重构时,我正在重新审视这一点。虽然它在实践中不会让任何事情变得更好,但我想知道是否有类似的东西:

member this.Describe =
    (lazy "the long to build text output").Value

能解决问题吗?因为惰性对象将被创建,但没有任何东西可以查询值本身。

这会可靠吗?

【问题讨论】:

  • 这里没有实际问题。
  • 我一直试图避免的问题是重新执行字符串的创建。
  • 啊,我明白了。我第一次读这篇文章时错过了重点。

标签: f# lazy-evaluation


【解决方案1】:

你声明属性的方式,它本质上是一个函数。它没有任何参数,但是每当有人试图读取它的值时,它的主体中的代码就会被执行。这就是属性在 .NET 中的一般工作方式。

这意味着无论您在其中放入什么,在每次访问时仍会执行。试试这个:

type T() =
  member this.Y = 
    printfn "Accessing value of Y"
    42

let t = T()
let a = t.Y
let b = t.Y
let c = t.Y

您应该会看到“访问 Y 的值”打印了 3 次。

如果你把整个东西包裹在lazy 中也没关系:你仍然在每次访问属性时构造一个全新的Lazy 对象,然后立即读取它的值,从而导致它的主体评估。

如果您真的想 (1) 推迟评估直到需要和/或 (2) 缓存评估值,您应该在属性主体的 outside 创建 Lazy 对象,然后将属性读取它的值,因此每次访问属性时都会读取相同的 Lazy 对象:

type T() =
  let x = lazy (
    printfn "Calculating value of X"
    "expensive computation"
  )

  member this.X = x.Value

let t = T()
let a = t.X
let b = t.X
let c = t.X

这将只打印一次“计算 X 的值”。

【讨论】:

    猜你喜欢
    • 2017-01-13
    • 2014-02-08
    • 2016-05-12
    • 1970-01-01
    • 2013-03-11
    • 2017-01-18
    • 2018-11-11
    • 2010-10-08
    相关资源
    最近更新 更多