11.3.4 F# 中的延迟值

11.3.4  F# 中的延迟值

 

    F# 中的延迟值(lazy value)是一种表示延迟的计算方式 ,这意味着,一个计算只在这个值需要时才被计算。在上一节中,我们在 C# 中使用函数实现了类似的事情,但是,延迟值只自动计算这个值一次,并记住结果。

    探索此功能的最佳方法是在 F# Interactive 中进行。清单 11.16 演示了一个如何使用它的脚本。

 

Listing 11.19 Introducing lazy values (F# Interactive)

 

> let foo(n) =
     printfn "foo(%d)" n
     n <= 10;;
val foo : int �C> bool

&gt; let n = lazy foo(10);;
val n : Lazy<bool> = Value is not created.

&gt; n.Value;;
foo(10)
val it : bool = true

&gt; n.Value;;
val it : bool = true

 

    我们首先写一个函数,它类似于我们的 C# 的 Foo 方法。这可以使我们跟踪计算是何时写到控制台的。第二个命令使用 F# 的 lazy 关键字。如果把一个表达式标记为 lazy,这个表达式不会立即计算,会被打包成延迟值。正如你从这个输出可以看到的,foo 函数还未调用,创建的值的类型 lazy<bool>。这表示一个延迟值,它可以计算出一个布尔值。

    在下一行,可以看到这个延迟值有一个成员,Value。这个属性计算出这个延迟值。在这里,就是调用 foo 函数。最后一个命令显示,再次访问 Value,不会重新计算。如果你看一下输出,可以看到 foo 函数并未调用。这就是说,我们可以清楚地看到,Lazy<'T> 是可变类型。如果我们使用无副作用的纯函数式的函数,作为其参数,我们就不能观察到。

    在前几章中,我们已经强调过这种函数式方式查看数据类型,我们已经看到,在处理一个类型时,知道需要哪种操作,是很有用的。让我们以这种观点来看一下延迟值。

 

使用操作指定延迟值

 

    如果 F# 语言没有 lazy 关键字,并且不允许我们写有属性的对象,可能会需要两个操作,一个用于构建延迟值,另一个提取实际值:

 

val create : (unit -&gt; 'T) -&gt; Lazy<'T>
val force : Lazy<'T> �C&gt; 'T

 

    正如你所看到的,create 操作的参数是一个函数值,它是在打包在 Lazy<'T> 内的值。在函数式编程中,存在表示延迟计算的其他类型,所以,当一个函数取一个 unit �C&gt; 'T 类型的函数作为参数值,并返回泛型类型,该类型可能表示延迟计算。force 操作的签名甚至更简单。它只是简单地提取实际值,从打包它的类型中。这个签名并不告诉我们实际值是如何打包的,但是,因为我们有 force 操作,就总是可以提取它。

    我们将在下一章中看到,在基本操作方面的一个类型的这种抽象描述,我们可以使用函数式编程中来处理是有用的。虽然 F# 提供了比使用函数更方便的方法处理延迟值,但是,了解基本操作(primitive operations)还是很有帮助的。

 

    当我们开始讨论延迟值时的动机,是我们可以不写自己的逻辑实现,或者操作符,只计算右边的参数值,它是否需要,何时需要。现在,再尝试用延迟值的新知识武装一下。

 

实现或和延迟或

 

    因为我们要实现一个运算符,将其定义为一个真正的运算符,而不只是作为通常的函数。就像在第 6 章中学过的,可以在 F# 中引入自己的运算符,所以,清单 11.17 显示两种不同形式的或运算符。

 

[没太注意,原书的清单序号可能有问题。从 11.14 开始,就变成了 11.17 了。]

 

Listing 11.17 Comparing eager and lazy or operators (F# Interactive)

 

let (||!) a b =
  if a then true
  elif b then true
  else false

// Using in F# Interactive
&gt; if (foo(5) ||! foo(7))
     then printfn "True";;
foo(5)
foo(7)
True

let (||?) (a:Lazy<_>)

  (b:Lazy<_>) =
  if a.Value then true
  elif b.Value then true
  else false

&gt; if lazy foo(5) ||? lazy foo(7)
     then printfn "True";;
foo(5)
True

 

    提前版本的运算符的参数值是布尔值,所以,我们可以在 if 条件中使用直接。延迟版本取打包计算的延迟值作为参数值,返回布尔值,要读取这个值,使用 Value 属性。

    当使用提前版的运算符时,我们指定参数值如正常一样。如输出所显示的,两个参数值都进行了计算。事实上,它们是在我们自定义运算符主体执行之前就计算了。当使用版时,我们加上了额外的 lqzy 关键字,以延迟这两个参数值。其结果是,只有一个表达式进行了计算,因为这对于最终的结果的计算已经足够了。

    本示例在很多方面只是出于一种好奇心,但它对于演示如何以编程方式实现元素的延迟是非常有用的,已经通过语言构造熟悉了。下一步我们将延迟值实现为类型,从 C# 中使用。这在语法结构上不是很紧凑,但即使是这样,仍是很有用的。

你可能感兴趣的:(职场,如何,休闲)