Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Julia function argument by reference

The docs say

In Julia, all arguments to functions are passed by reference.

so I was quite surprised to see a difference in the behaviour of these two functions:

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 

here is the unexpectedly different output:

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

if the array is passed by reference, I would have expected foo! to change the zeros to ones.

like image 220
Lindon Avatar asked Feb 06 '16 00:02

Lindon


4 Answers

r=r+1 is an Assignment statement, this means it reallocates r, so it no longer refers to its pair in the parent scope. but r[i]=r[i]+1 Mutates r value, mutation is differ from assignment (a good description here), and after that r still refers to its pair variable in the parent scope.

like image 109
Reza Afzalan Avatar answered Nov 17 '22 05:11

Reza Afzalan


I think the document is a bit vague here.

Strictly speaking, Julia is "call-by-value where the value is a reference" , or "call-by-sharing", as used by most languages such as python, java, ruby, js... See wiki

A call by reference behaviour would indeed make foo! to change the zeros to ones. However Julia doesn't support that. (If you know C#, that is what ref or out does)

like image 22
colinfang Avatar answered Nov 17 '22 03:11

colinfang


In order to mutate each variable inside an array, the broadcast . operation can be used. But be aware that each value inside the array will be changed equally thus there is no need for a for loop.

In the case of adding 1 to each element of an array :

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]

Despite this, if each value of an array is needed to be changed independently then for loop with indices is inevitable.

like image 7
Kadir Gunel Avatar answered Nov 17 '22 03:11

Kadir Gunel


In practice, regardless of theory(call by sharing), described in previous answer, everything happens in Julia as if pointer variables as arrays were passed by reference, while scalar variables, such as numbers, were passed by value.

This is a pain for people like me, used to C or Pascal languages, where one specify in formal parameter part in function declaration, if the parameter is by value or by reference.

However, due to Julia's feature that allows return of multiple values in a function, there is an elegant way to simulate parameters by reference for scalar variables. Obviously this works even for immutable variables like strings, since the variables are recreated.

Julia's Code

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)

Display

Before: 4 7
After: 11 49 3773 

a and b was both changed inside function process. a was added to 7 and b was multiplied by 7

like image 3
Paulo Buchsbaum Avatar answered Nov 17 '22 04:11

Paulo Buchsbaum