【问题标题】:Passing large sequences as function parameters将大序列作为函数参数传递
【发布时间】:2012-06-28 18:36:43
【问题描述】:

这确实是一个简单的问题(但我似乎无法在 MSDN 文档中找到答案)。

如果我在 F# 中将大型序列和对象作为函数参数传递,除非我提供 byref 关键字,否则它们是否总是按值复制?问题是,我无意修改参数,但同时,我不希望每次调用函数时都复制大对象。

【问题讨论】:

  • 我强烈认为任何说“数组通过引用传递”的人都在滥用术语,这可能会使新学习者感到困惑,但承认我的感受可能是少数。

标签: f# pass-by-reference pass-by-value


【解决方案1】:

默认情况下,序列/列表/数组通过引用传递

例如

let mutate (arr:int[]) =
    arr.[0] <- 1

let t = Array.zerocreate 1
mutate t
printfn "%i" (t.[0]) //prints 1

【讨论】:

  • 如果默认情况下,你的意思是有一些不同的方法吗?
  • @svick - 如果你真的想使用结构体并用它来伪造数组,你可能会做一些讨厌的事情
  • 那么说一个与 C# 中非常相似的概念(其中有值类型和引用类型)也适用于这里是否正确?堆上分配的东西总是引用类型吗?
  • @Shredderroy 就像在 C# 中一样,F# 具有引用类型和值类型。就像在 C# 中一样,值和引用类型都可以根据上下文在堆上分配。
【解决方案2】:

由于值是不可变的,因此您传递的相同序列将被修改或用于.. 不会每次都复制对象..

虽然如果您修改序列,那么将使用新值创建一个新序列

【讨论】:

    【解决方案3】:

    如 cmets 中所述,F# 使用与 C# 基本相同的机制。这意味着只有 值类型(原始值,如 intfloat 和结构)被复制到堆栈上,所有其他类型通过引用传递 - 这包括所有集合(序列 @987654323 @、数组 'T[] 以及不可变的函数列表 list&lt;'T&gt;)。

    当您使用不可变类型时(函数式 list&lt;'T&gt;seq&lt;'T&gt; 没有副作用),除了性能之外,传递引用和复制对象没有区别 - 运行时总是将它们作为引用传递,但是除非您正在优化某些程序,否则您无需担心这一点(如果您可以更改传递机制,它不会改变行为)。

    对于(可变)数组,通过查看 John Palmer 回答中的示例,您可以很容易地看到行为是通过引用传递的。

    作为旁注,如果要创建对堆栈分配的可变变量的引用,则使用 byref 关键字(或类型)。这意味着,您可以使用它来传递对值类型变量的引用:

    let bar (a:byref<int>) =    // takes reference to a (stack-allocated) 'int' variable
      a <- 42                   // mutate the variable
    
    let mutable foo = 10        // declare a mutable variable
    bar &foo                    // pass reference to the `bar` function
    

    如果您使用byref 传递集合,您实际上是在传递对堆栈分配的引用的引用,该引用实际上指向堆分配的数据结构。但是,byref 在 F# 中很少使用 - 在调用 C/C++ 或 COM 库时,您主要需要它来实现互操作性。

    【讨论】:

    • 非常感谢您的回复。非常感谢所有文章和书籍,这些都是我学习 F# 的主要资源。但由于约翰·帕尔默是第一个回答基本问题的人,我觉得在道德上有义务接受他的回答。
    • @Shredderroy 我很高兴我的一些工作很有用!约翰肯定给了你正确的答案(+1!)-我只是想我可以添加更多细节。
    • "和所有其他类型都是通过引用传递" 不,所有其他类型都是引用类型,其值(引用)也是通过值传递的。
    【解决方案4】:

    我不同意人们在这里使用的术语。

    我将其表述为:默认情况下,.NET 中的所有内容都是按值传递的,例外情况是,例如C# 中的 refout 参数和 F# 中的 byref 参数。但是,序列和数组以及大多数其他类型(除了结构体和基元)都是对象引用,这意味着当您将它们作为参数(按值)传递时,您仅传递对实体的引用,调用者和被调用者共享堆上的相同实际实体。所以引用是按值传递的,但引用本身只是小东西,而它们指向的对象实体(包含所有数据)是在堆上共享的大东西。

    【讨论】:

      猜你喜欢
      • 2022-01-06
      • 2019-07-23
      • 1970-01-01
      • 1970-01-01
      • 2021-04-13
      • 1970-01-01
      • 1970-01-01
      • 2023-04-02
      相关资源
      最近更新 更多