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

Julia functions: making mutable types immutable

来自 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

这里有一点要回应。

首先,Julia 不按引用传递或按值传递。相反,它采用了一种称为传递共享的范例。引用 the docs:

Function arguments themselves act as new variable bindings (new locations that can refer to values), but the values they refer to are identical to the passed values.

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

这有一些有趣的副作用。例如,您会注意到许多成熟的 Julia 包(以及基本代码)都包含许多短函数。这种代码结构是函数调用开销接近零的直接结果。另一方面,像 Mathematica 和 MatLab 这样的语言倾向于 wards 长函数。我不想在这里引起轰动war,所以我只是声明我个人更喜欢许多短函数的 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!.

等函数