【问题标题】:Mutable variables in closures in F#F# 闭包中的可变变量
【发布时间】:2012-06-08 10:40:24
【问题描述】:

我正在尝试从 java do F# 中移植一些代码,以在给定点周围生成多维点网格。我想出了这个:

let gridGenerator midpoint stepSize steps = 
    seq {
        let n = Array.length midpoint
        let direction = Array.create n -steps
        let mutable lastIndex = n-1
        while lastIndex>=0 do
            let next = midpoint |> Array.mapi (fun i x -> x+ direction.[i]*stepSize)
            while lastIndex>=0 && direction.[lastIndex]=steps do 
                direction.[lastIndex]<- (-steps)
                lastIndex<-lastIndex-1;        
            if lastIndex>=0 then
                direction.[lastIndex]<-direction.[lastIndex]+1;
                lastIndex <- n-1;
            yield next;
    }

除了这段代码非常必要(我会很感激如何修复它的提示),我得到一个编译错误:

Program.fs(18,15):错误 FS0407:可变变量“lastIndex”的使用方式无效。可变变量不能被闭包捕获。考虑通过“ref”和“!”消除这种突变的使用或使用堆分配的可变参考单元。

如何解决此错误?如何让它更实用?

示例:对于中点 [|0.0, 1.0|],步长 0.5 和步长 1 我期望(实际上以任何顺序)

seq{[|-0.5, 0.5|], [|-0.5, 1.0|], [|-0.5, 1.5|], [|0.0, 0.5|], [|0.0, 1.0|], [|0.0, 1.5|], [|0.5, 0.5|], [|0.5, 1.0|], [|0.5, 1.5|]}

还请注意,这将执行多次,因此性能至关重要。

【问题讨论】:

  • “可变变量不能被闭包捕获。” F# 的严肃设计 WTF。
  • 错误信息告诉你该怎么做:使用ref
  • @leppie - 这避免了 C# 中可能发生的一整类微妙的错误 - 例如参见 eric lipperts blog
  • @leppie 如果您不需要手动操作,为什么不用 C++ 编写代码?更好的是汇编代码——那里没有手持。
  • @leppie,有关设计原理,请参阅 lorgonblog.wordpress.com/2008/11/12/…

标签: f#


【解决方案1】:
let gridGenerator midpoint stepSize steps =
    seq {
        let n = Array.length midpoint
        let direction = Array.create n -steps
        let lastIndex = ref (n - 1)
        while !lastIndex >= 0 do
            let next = midpoint |> Array.mapi (fun i x -> x + direction.[i] * stepSize)
            while !lastIndex >= 0 && direction.[!lastIndex] = steps do
                direction.[!lastIndex] <- -steps
                decr lastIndex
            if !lastIndex >= 0 then
                direction.[!lastIndex] <- direction.[!lastIndex] + 1
                lastIndex := n - 1
            yield next
    }

?

ref 非常适合此类用途,并且不被视为可变变量(因为它们不是)。

【讨论】:

  • 所以ref 只是一个盒子(在功能术语中)?
  • 编译。但是为什么它到处都有类型 int 呢?我认为 F# 应该能够泛化类型,而不是猜测 int?
  • 0 和 1 的类型为 int,默认的 decr 函数也仅适用于 int 的类型。我还认为数组索引必须是整数(.NET 框架设计中的一个错误)。
【解决方案2】:

这是一种更实用的方法:

let rec gridGenerator midpoint stepSize steps =
    match midpoint with
    | [] -> Seq.singleton []
    | p::point ->
        seq {
            for d in - stepSize * steps .. stepSize .. stepSize * steps do
                for q in gridGenerator point stepSize steps do
                    yield (p + d) :: q
        }

还有签名:

val gridGenerator : int list -> int -> int -> seq<int list>

如果您重复使用结果,请记住将其缓存或转换(为数组或列表)。

【讨论】:

    【解决方案3】:

    现在你可以只使用 F# 4,它没有这样的限制。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-10-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-21
      • 2015-09-18
      • 1970-01-01
      相关资源
      最近更新 更多