Julia函数:使可变类型不可变

达里奥·罗萨(Dario Rosa)

来自Wolfram Mathematica,我喜欢这样的想法:每当我将变量传递给函数时,我都会有效地创建该变量的副本。另一方面,我了解到Julia中存在可变和不可变类型的概念,前者通过引用传递,后者通过值传递。有人可以解释一下这种区别的好处吗?为什么数组通过引用传递?我天真地认为这是一个坏方面,因为它会产生副作用并破坏编写纯功能代码的可能性。我的推理哪里错了?有没有一种方法可以使数组不可变,以便在将其传递给函数时按值有效传递?

这是一个代码示例

#x is an in INT and so is immutable: it is passed by value
x = 10
function change_value(x)
    x = 17
end

change_value(x)

println(x)

#arrays are mutable: they are passed by reference
arr = [1, 2, 3]

function change_array!(A)
    A[1] = 20
end

change_array!(arr)

println(arr)

确实修改了数组 arr

科林·T·鲍尔斯

这里有很多回应。

首先,Julia不会传递引用或传递值。相反,它采用了一种称为传递共享的范例。引用文档

函数参数本身充当新的变量绑定(可以引用值的新位置),但是它们引用的值与传递的值相同。

其次,您似乎在问为什么在将数组传递给函数时Julia不会复制数组。这是一个简单的答案:性能。Julia是一种面向性能的语言。每次将数组传递给函数时都进行复制会降低性能。每次copy操作都需要时间。

这有一些有趣的副作用。例如,您会注意到许多成熟的Julia软件包(以及基本代码)都包含许多短函数。此代码结构是函数调用的开销几乎为零的直接结果。另一方面,诸如Mathematica和MatLab之类的语言倾向于长函数。我不想在这里开始一场火焰大战,所以我只说我个人比较喜欢许多短函数的Julia风格。

第三,您想知道共享共享的潜在负面影响。从理论上讲,您是正确的,当用户不确定某个函数是否会修改其输入时,这可能会导致问题。在语言的早期,人们对此进行了长时间的讨论,根据您的问题,您似乎已经知道,约定是修改其参数!的函数在函数名中带有尾随有趣的是,该标准不是强制性的,因此是的,从理论上讲,最终可能会出现狂野西部类型的情况,即用户生活在恒定的不确定状态中。实际上,就我所知,这从来不是问题。使用约定!是在Base Julia中强制执行的,实际上我从未遇到过不遵守该约定的软件包。总而言之,是的,通过共享时可能会遇到问题,但实际上这从来都不是问题,而性能收益远远超过成本。

第四(也是最后),您问是否有一种使数组不变的方法。首先,我强烈建议您防止黑客尝试使本机数组不可变。例如,您可以尝试禁用setindex!数组功能...但是请不要这样做。它会破坏很多东西。

如问题评论中所述,您可以使用StaticArrays但是,正如Simeon在此答案的注释中指出的那样,对于大型数据集使用静态数组会产生性能损失。超过100个元素,您可能会遇到编译问题。静态数组的主要好处实际上是可以为较小的静态数组实现的优化。

phipsgabler在下面的注释中建议的另一个基于包的选项是FunctionalCollections尽管看起来只是偶尔维护,但这似乎可以满足您的要求。当然,这并不总是一件坏事。

一种更简单的方法是,只要您想实现传递值,就可以使用自己的代码复制数组。例如:

f!(copy(x))

只要确保你明白之间的差别copydeepcopy,当你可能需要使用后者。如果只使用数字数组,则永远不需要数字数组,实际上使用它可能会大大降低代码速度。

如果您想做一些工作,那么您也可以本着静态数组的精神来构建自己的数组类型,但是不需要静态数组所带来的所有麻烦。例如:

struct MyImmutableArray{T,N}
    x::Array{T,N}
end
Base.getindex(y::MyImmutableArray, inds...) = getindex(y.x, inds...)

同样,您可以在此类型中添加所需的其他任何功能,但不包括setindex!

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章