Dar*_*osa 9 mutable immutability julia
来自 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)
Run Code Online (Sandbox Code Playgroud)
这确实修改了数组 arr
Col*_*ers 15
这里有一个公平的回应。
首先,Julia 不传递引用或传递值。相反,它采用了一种称为传递共享的范式。引用文档:
函数参数本身充当新的变量绑定(可以引用值的新位置),但它们引用的值与传递的值相同。
其次,您似乎在问为什么 Julia 在将数组传递给函数时不复制它们。这是一个简单的答案:性能。Julia 是一种面向性能的语言。每次将数组传递给函数时都进行复制对性能不利。每个copy操作都需要时间。
这有一些有趣的副作用。例如,您会注意到许多成熟的 Julia 包(以及基础代码)由许多短函数组成。这种代码结构是函数调用接近零开销的直接结果。另一方面,像 Mathematica 和 MatLab 这样的语言倾向于长函数。我不想在这里开始一场激烈的战争,所以我只是说我个人更喜欢许多短函数的 Julia 风格。
第三,您想知道传递共享的潜在负面影响。从理论上讲,当用户不确定某个函数是否会修改其输入时,这可能会导致问题,这一点是正确的。在该语言的早期,对此进行了长时间的讨论,根据您的问题,您似乎已经确定约定是修改其参数!的函数在函数名称中带有尾随。有趣的是,这个标准不是强制性的,所以是的,理论上有可能最终出现一个狂野的西部类型的场景,其中用户生活在一个持续的不确定状态中。在实践中,这从来都不是问题(据我所知)。使用约定!在 Base Julia 中强制执行,实际上我从未遇到过不遵守此约定的包。综上所述,是的,pass-by-share 时可能会遇到问题,但在实践中从来没有出现过问题,而且性能收益远远超过成本。
第四(也是最后),您询问是否有办法使数组不可变。首先,我强烈建议不要尝试使本机数组不可变的黑客行为。例如,您可以尝试禁用setindex!数组的功能...但请不要这样做。它会破坏很多东西。
正如对该问题的评论中提到的,您可以使用StaticArrays。然而,正如 Simeon 在对这个答案的评论中指出的那样,对于真正大的数据集使用静态数组会导致性能下降。超过 100 个元素,您可能会遇到编译问题。静态数组的主要好处实际上是可以为较小的静态数组实现的优化。
phipsgabler 在下面的评论中建议的另一个基于包的选项是FunctionalCollections。这似乎可以满足您的需求,尽管它看起来只是偶尔维护。当然,这并不总是一件坏事。
一种更简单的方法是,只要您想实现传值,就在您自己的代码中复制数组。例如:
f!(copy(x))
Run Code Online (Sandbox Code Playgroud)
只要确保你明白之间的差别copy和deepcopy,当你可能需要使用后者。如果您只处理数字数组,则永远不需要后者,实际上使用它可能会大大减慢您的代码速度。
如果您想做一些工作,那么您也可以本着静态数组的精神构建自己的数组类型,但没有静态数组所需的所有花里胡哨。例如:
struct MyImmutableArray{T,N}
x::Array{T,N}
end
Base.getindex(y::MyImmutableArray, inds...) = getindex(y.x, inds...)
Run Code Online (Sandbox Code Playgroud)
同样,您可以向此类型添加您想要的任何其他函数,同时排除setindex!.
| 归档时间: |
|
| 查看次数: |
476 次 |
| 最近记录: |