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

问题描述 投票:5回答:1

来自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 immutability mutable
1个回答
7
投票

这里有很多回应。

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

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

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

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

[第三,您想知道路过共享的潜在负面影响。 理论上您是正确的,当用户不确定某个函数是否会修改其输入时,这可能会导致问题。在语言的早期,对此进行了长时间的讨论,根据您的问题,您似乎已经知道,约定是修改其参数的函数在函数名称中带有尾随!。有趣的是,此标准是[[非强制性]],所以是的,[[理论上]]最终可能会出现狂野西部类型的情况,即用户处于不确定的恒定状态。 在实践中(据我所知)这从来不是问题。使用!的约定是在Base Julia中强制执行的,实际上,我从未遇到过不遵守该约定的软件包。总而言之,是的,通过共享时可能会遇到问题,但实际上从来都不是问题,并且性能收益远超过成本。第四(也是最后),您问是否有一种使数组不变的方法。首先,我强烈建议您防止黑客尝试使本机数组不可变。例如,您可以尝试禁用数组的setindex!功能...但是请不要这样做。它会破坏很多东西。如对该问题的评论中所述,您可以使用StaticArrays。但是,正如Simeon在对此答案的评论中指出的那样,对于真正的大型数据集使用静态数组会产生性能损失。超过100个元素,您可能会遇到编译问题。静态数组的主要好处实际上是可以为较小的静态数组实现的优化。

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

f!(copy(x))

请确保您了解copydeepcopy之间的区别,以及何时需要使用后者。如果只使用数字数组,则永远不需要数字数组,实际上使用它可能会极大地降低代码速度。

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

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

并且类似地,您可以将所需的任何其他功能添加到此类型,同时排除诸如setindex!之类的功能。

© www.soinside.com 2019 - 2024. All rights reserved.