89

首先,我承认这个问题可能是重复的;让我知道。

我很好奇在需要可变性的情况下,一般的“最佳实践”是什么。F# 似乎为此提供了两种工具:let mutable绑定,它似乎像“大多数”语言中的变量一样工作,以及ref需要显式取消引用才能使用的引用单元(使用函数创建)。

有几种情况是“强制”进入其中一种情况:.NET 互操作倾向于使用可变的 with <-,而在工作流计算中必须使用refwith :=。所以这些案例非常明确,但我很好奇在这些场景之外创建自己的可变变量时该怎么做。一种风格比另一种风格有什么优势?(也许对实施的进一步了解会有所帮助。)

谢谢!

4

5 回答 5

137

我只能支持gradbot所说的——当我需要突变时,我更喜欢let mutable.

关于两者之间的实现和差异 -ref单元格本质上是通过一个非常简单的记录来实现的,该记录包含一个可变记录字段。您可以自己轻松编写它们:

type ref<'T> =  // '
  { mutable value : 'T } // '

// the ref function, ! and := operators look like this:
let (!) (a:ref<_>) = a.value
let (:=) (a:ref<_>) v = a.value <- v
let ref v = { value = v }

这两种方法之间的一个显着区别是,let mutable将可变值存储在堆栈中(作为 C# 中的可变变量),而ref将可变值存储在堆分配记录的字段中。这可能会对性能产生一些影响,但我没有任何数字......

多亏了这一点,使用的可变值ref可以被别名 - 这意味着您可以创建两个引用相同可变值的值:

let a = ref 5  // allocates a new record on the heap
let b = a      // b references the same record
b := 10        // modifies the value of 'a' as well!

let mutable a = 5 // mutable value on the stack
let mutable b = a // new mutable value initialized to current value of 'a'
b <- 10           // modifies the value of 'b' only!
于 2010-07-10T23:54:44.317 回答
19

相关问题: “您提到局部可变值不能被闭包捕获,因此您需要使用 ref 代替。原因是闭包中捕获的可变值需要在堆上分配(因为闭包分配在堆)。” 来自F# ref-mutable vars 与对象字段

我认为let mutable比参考细胞更受欢迎。我个人只在需要时使用参考单元。

由于递归和尾调用,我编写的大多数代码不使用可变变量。如果我有一组可变数据,我会使用记录。对于我let mutable用来制作私有可变变量的对象。我只真正将参考单元用于闭包,通常是事件。

于 2010-07-10T23:46:36.060 回答
11

这篇 MSDN 博客文章中可变值的简化使用部分中所述,您不再需要 lambda 的引用单元格。所以总的来说,你根本不再需要它们。

于 2016-09-04T10:59:08.280 回答
5

Brian 的这篇文章可能会提供答案。

可变变量易于使用且高效(无包装),但无法在 lambdas 中捕获。可以捕获Ref 单元格,但冗长且效率较低(? - 不确定)。

于 2010-07-11T11:28:12.957 回答
3

您可能想查看 wikibook 中的Mutable Data部分。

为方便起见,这里有一些相关的报价:

mutable 关键字经常与记录类型一起使用以创建可变记录

可变变量在一定程度上受到限制:在定义它们的函数范围之外,可变变量是不可访问的。具体来说,这意味着它不可能在另一个函数的子函数中引用一个可变的。

Ref 单元格绕过了可变对象的一些限制。事实上,ref 单元格是非常简单的数据类型,它将可变字段包装在记录类型中。

由于 ref 单元是在堆上分配的,因此它们可以在多个函数之间共享

于 2010-07-10T23:53:59.280 回答