【问题标题】:Julia function argument by reference引用的 Julia 函数参数
【发布时间】:2016-05-16 02:32:33
【问题描述】:

文档说

在 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!将零更改为一。

【问题讨论】:

  • 您可能看到的docs 在最近的版本中是corrected。他们现在陈述了正确的评估策略(通过分享)而不是通过引用。

标签: reference pass-by-reference julia


【解决方案1】:

r=r+1 是一个 Assignment 语句,这意味着它重新分配了r,因此它不再引用它在父作用域中的对。但是r[i]=r[i]+1 变异 r值,变异不同于赋值(a good description here),之后r仍然引用其父作用域中的pair变量。

【讨论】:

    【解决方案2】:

    我觉得这里的文档有点含糊。

    严格来说,Julia 是“按值调用,其中值是引用”,或“按共享调用”,如大多数语言(如 python、java、ruby、js)所使用的......见@ 987654321@

    引用行为的调用确实会使foo! 将零更改为一。然而,朱莉娅不支持这一点。 (如果你知道 C#,那是 refout 所做的)

    【讨论】:

    • 我还看到了“call-by-pointer”(或“pass-by-pointer”)这个词用来指代这个,我认为它很简短。无论如何,总是值得提醒的是,这些术语有时用于语言理论,有时用于实践。而且这些含义并不总是一致的,甚至在一个学科内因作者而异!
    • "call-by-pointer-value" 更加清晰,因为它避免了特别是与 C++ 和 Rust 引用的混淆。可以说,将 Julia 称为“按引用传递”是不正确的,因为它总是按值传递,如果参数是对象,那么值就是指针。 C++、Rust、PL/SQL、Swift(我想从技术上讲是 C 和汇编程序)具有传递引用的能力,这是传递指向对象的指针的独特操作。完全调用对象句柄/指针“引用”会引起混淆。
    【解决方案3】:

    为了改变数组中的每个变量,可以使用广播. 操作。但请注意,数组中的每个值都将被同等更改,因此不需要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 循环是不可避免的。

    【讨论】:

      【解决方案4】:

      在实践中,无论理论如何(调用by sharing),如上一个答案中所述,一切都发生在 Julia 中,就好像将指针变量作为数组传递了 by reference,而标量变量(例如数字)传递了 by value .

      对于像我这样习惯于 C 或 Pascal 语言的人来说,这是一种痛苦,如果参数是 by valueby 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 
      

      ab 都在函数进程内部进行了更改。 a 加到 7,b 乘以 7

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-09
        • 2019-12-07
        • 2017-11-26
        • 2021-10-04
        相关资源
        最近更新 更多