在函数之间传递大数据结构的F#效率含义

时间:2016-04-25 04:55:18

标签: performance f# pass-by-value

F#如何将数据从调用函数传递给被调用函数?它是在复制数据之前复制数据还是只传递指针?我会想到后者但想确定。 在相关的说明中,以下2个F#代码样式是否存在任何性能影响。

let someFunction e =
    1//pretend this is a complicated function

let someOtherFunction e =
    2//pretend this is a complicated function

let foo f largeList=
    List.map (fun elem -> f elem)

let bar largeList =
    largeList
    |> foo someFunction
    |> foo someOtherFunction


let bar2 largeList =
    let foo2 f f2 =
        largeList
        |> List.map (fun elem -> f elem)
        |> List.map (fun elem -> f2 elem)
    foo2 someFunction someOtherFunction

你是否希望bar与bar2有不同的表现?如果没有,是否有任何我应该注意的情况会产生影响?

1 个答案:

答案 0 :(得分:3)

答案简短:

没有。不复制整个列表,只是对它的引用。

答案很长:

在F#中(就像在C#中一样),值和引用类型都可以通过值或引用传递。

默认情况下,值类型和引用类型都按值传递。

  • 在值类型(结构)的情况下,这意味着你将成为 传递整个数据结构的副本。

  • 对于引用类型(类,有区别的联合,记录等),这意味着引用按值传递。这并不意味着复制整个数据结构,只是意味着复制引用数据结构的int / int64

如果您正在使用可变数据结构,例如ResizeArray<'T>(.NET List<'T>)是类,按值传递引用可能会产生影响。例如,您传递给它的功能可能是将元素添加到列表中吗?此类更新将适用于从两个位置引用的数据结构。既然您的问题使用了不可变的F#List,那么您不必担心这个!

您还可以通过引用传递值/引用类型,有关详细信息,请参阅:https://msdn.microsoft.com/en-us/library/dd233213.aspx#Anchor_4

F#list实现为单链表,这意味着访问头和前置操作是O(1)。这些数据结构也非常节省内存,因为当您将一个元素添加到列表中时,您只需要存储新值和对列表其余部分的引用。

所以你可以看到它是如何工作的,这样的数据结构可以像这样实现:

type ExampleList<'T> = 
    |Empty
    |Cons of 'T * List<'T>

其他信息:

热切地评估

List.map意味着每次调用它时,都会创建一个新列表。如果您使用Seq.map(F#List实现IEnumerable<'T>接口),这是懒惰评估的,您可以仅在列表的枚举中评估两个地图操作。

largeList
|> Seq.map (fun elem -> f elem)
|> Seq.map (fun elem -> f2 elem)
|> List.ofSeq

对于大型列表而言,这可能会更有效率,因为它只涉及分配一个新的结果列表,而不是两个。