是否可以在F#中使用正常的订单评估

时间:2013-03-28 12:29:30

标签: f# operator-precedence

在Scala中,您可以选择应用或正常的订单评估,有关示例,请参阅“Scala call-by-name (=>) vs call-by-type”。

  def byName(a: => Unit) = {
    for (i <- 0 until 10) {println(a)}

  }

  def byValue(a: Unit) = {
    for (i <- 0 until 10) {println(a)}

  }

  var i = 1;

  byValue(i = i + 1)
  println(i); // 2

  byName(i = i + 1)
  println(i) // 12

在F#中是否可以相同?

2 个答案:

答案 0 :(得分:4)

正如李指出的那样,F#不允许你为函数的参数指定评估策略。这当然是一个有用的功能,但我认为它有时可能会让人感到困惑 - 例如,如果你有一个像int -> int这样的第一类功能,那么这个类型并没有告诉你使用什么样的评估策略,所以你要么使类型更复杂或将其限制为命名函数。

除了明确使用lambda函数之外,F#还使用lazy关键字和Lazy<'T>类型提供对延迟评估的支持(即,懒惰评估,但缓存结果):

let foo (a:Lazy<int>) (b:Lazy<int>) = 
  if a.Value = 0 then 0
  else b.Value

仅当第一个参数非零时,此函数才会计算第二个参数:

foo (lazy (printfn "a"; 0)) (lazy (printfn "b"; 10))   // Prints just 'a'
foo (lazy (printfn "a"; 10)) (lazy (printfn "b"; 10))  // Prints both 'a' and 'b'

这在语法上比使用函数更轻量级,但它仍然需要在调用站点上进行明确规范,而不仅仅是在声明站点上。

答案 1 :(得分:3)

据我所知,没有内置的支持,所以最接近的是通过用函数延迟计算来模拟它。

let cnst a b = a
let apply (f: unit -> unit) = Seq.iter (fun i -> printfn "%A" (f())) [1..10]
let byName (f: unit -> unit) = apply (cnst (f()))
let byValue (f: unit -> unit) = apply f

然后你的例子是:

let i = ref 1
byValue (fun _ -> do i := !i + 1)

printfn "%d" !i

i := 1
byName (fun _ -> do i := !i + 1)
printfn "%d" !i