【问题标题】:F#: let mutable vs. refF#:让 mutable 与 ref
【发布时间】:2011-03-14 08:48:48
【问题描述】:

首先,我承认这个问题可能是重复的;请告诉我。

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

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

谢谢!

【问题讨论】:

标签: f# mutable


【解决方案1】:

相关问题: “你提到局部可变值不能被闭包捕获,所以你需要使用 ref 代替。原因是闭包中捕获的可变值需要在堆上分配(因为闭包是在堆上分配的)。”来自F# ref-mutable vars vs object fields

我认为let mutable 优于参考单元格。我个人只在需要时使用参考单元格。

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

【讨论】:

    【解决方案2】:

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

    为方便起见,以下是一些相关的引用:

    mutable 关键字经常被使用 使用记录类型创建可变的 记录

    可变变量有点 有限的:可变的是不可访问的 超出功能范围 它们被定义的地方。具体来说, 这意味着它不可能 在子函数中引用一个可变的 另一个函数。

    参考单元绕过一些 可变变量的限制。事实上,参考 单元格是非常简单的数据类型 在记录中包装一个可变字段 输入。

    由于 ref 单元被分配在 堆,它们可以共享 多种功能

    【讨论】:

      【解决方案3】:

      我只能支持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!
      

      【讨论】:

      • 只是提醒一下:在堆栈或堆上是一个实现细节,与问题并不完全相关(但仍然是很好的答案)
      • 我认为,在决定什么是最佳实践时,了解某事是否会引起堆分配和收集的开销非常重要。
      • @jackmott 查看 Eric Lippert 的这篇文章,名为 The Stack Is An Implementation Detail
      • @jaromey 是的,我知道我在这一点上完全不同意 eric。在考虑什么是“最佳实践”时,您不能将性能考虑放在一边。人们经常声称性能并不重要,但是由于一千个实现细节的死亡,所以很多软件都很慢。
      【解决方案4】:

      This article by Brian 可能会提供答案。

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

      【讨论】:

      • "(...) 并且效率较低" - 包装器类型可能会占用更多内存。
      • 这已经改变了,因为 F# 4.0 mutable 在大多数情况下都可以被捕获,并且现在对 ref 的需求要少得多。
      【解决方案5】:

      正如this MSDN Blog article可变值的简化使用 部分中所述,您不再需要 lambda 的 ref 单元格。 所以总的来说,你根本不再需要它们。

      【讨论】:

        猜你喜欢
        • 2011-02-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-12-18
        • 2010-10-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多