文档说
在 Julia 中,函数的所有参数都是通过引用传递的。
所以我很惊讶地看到这两个函数的行为有所不同:
function foo!(r::Array{Int64})
r=r+1
end
function foobar!(r::Array{Int64})
for i=1:length(r)
r[i]=r[i]+1
end
end
这是意外不同的输出:
julia> myarray
2-element Array{Int64,1}:
0
0
julia> foo!(myarray);
julia> myarray
2-element Array{Int64,1}:
0
0
julia> foobar!(myarray);
julia> myarray
2-element Array{Int64,1}:
1
1
如果数组是通过引用传递的,我会期望 foo! 将零更改为 1。
r=r+1
是一个 赋值语句,这意味着它重新分配r
,因此它不再在父作用域中引用它的对。 但是r[i]=r[i]+1
突变 r 值,突变与赋值不同(这里有一个很好的描述),之后r
仍然在父作用域中引用它的对变量。
我认为这里的文档有点模糊。
严格来说,Julia 是"按值调用,其中值是引用",或"按共享调用",正如大多数语言(如 python、java、ruby、js)使用的那样......查看维基
通过引用行为调用确实会foo!
将零更改为 1。然而,朱莉娅不支持这一点。(如果你知道C#,那就是ref
或out
所做的)
为了改变数组中的每个变量,可以使用广播.
操作。但请注意,数组中的每个值都将平等地更改,因此不需要for
循环。
在向数组的每个元素添加 1 的情况下:
a = rand(1:10, 10)
show(a) = [4, 8, 9, 1, 4, 2, 6, 7, 1, 5]
function add1!(a::Array{Int64})
a .= a .+ 1
end
add1!(a);
show(a) = [5, 9, 10, 2, 5, 3, 7, 8, 2, 6]
尽管如此,如果需要独立更改数组的每个值,那么for
索引循环是不可避免的。
在实践中,无论前面的答案中描述的理论(调用by sharing
),一切都发生在 Julia 中,就好像指针变量作为数组被传递by reference
,而标量变量,如数字,被传递by value
。
对于像我这样习惯了 C 或 Pascal 语言的人来说,这是一种痛苦,在函数声明中指定形式参数部分,如果参数是by value
还是by reference
。
但是,由于 Julia 的功能允许在函数中返回多个值,因此有一种优雅的方法来模拟标量变量的参数by reference
。显然,这甚至适用于字符串等不可变变量,因为变量是重新创建的。
朱莉娅的密码
function process(a,b,c)
a += c
b *= c
return a*b*c, a, b # normal result, changing "a" and "b"
end
a = 4
b = 7
println("Before: ", a," ",b)
result, a, b = process(a,b,7)
println("After: ", a," ",b," ", result)
显示
Before: 4 7
After: 11 49 3773
a
和b
都在函数过程中进行了更改。 a
加到 7,b
乘以 7