将大序列作为函数参数传递

时间:2012-06-27 10:40:00

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

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

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

4 个答案:

答案 0 :(得分:10)

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

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

答案 1 :(得分:5)

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

当你使用不可变类型(函数list<'T>seq<'T>没有副作用)时,传递引用和复制除了性能之外的对象之间没有区别 - 运行时总是通过它们作为参考,但你不需要担心这个,除非你正在优化一些程序(如果你可以改变传递机制,它不会改变行为)。

对于(可变)数组,您可以通过查看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库时,您主要需要它来实现互操作性。

答案 2 :(得分:2)

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

例如

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

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

答案 3 :(得分:0)

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

虽然如果您修改了Sequence,那么将使用新值

创建一个新序列