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有不同的表现?如果没有,是否有任何我应该注意的情况会产生影响?
答案 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
对于大型列表而言,这可能会更有效率,因为它只涉及分配一个新的结果列表,而不是两个。