Reputation: 20774
This is similar to this question:
Add a row to a matrix in Julia?
But now I want to grow the matrix inside a function:
function f(mat)
mat = vcat(mat, [1 2 3])
end
Now, outside this function:
mat = [2 3 4]
f(mat)
But this doesn't work. The changes made to mat
inside f
aren't propagated outside, because a new mat
was created inside f
(see http://docs.julialang.org/en/release-0.4/manual/faq/#functions).
Is it possible to do what I want?
Upvotes: 2
Views: 266
Reputation: 22215
I agree with Fengyang's very comprehensive answer and excellent avoidance of an XY Problem. Just adding my 2¢.
push!
ing is a fairly expensive operation, since at each push a new location in memory needs to be found to accommodate the new variable of larger size.
If you care about efficiency, then it would be a lot more prudent to preallocate your mat
and mutate it (i.e. alter its contents) inside your function, which is permitted, i.e.
julia> mat = Array(Int64, (100,3)); # mat[1,:] contains garbage values
julia> f(mat, ind, x) = mat[ind,:] = x;
julia> f(mat, 1, [1 2 3]); # mat[1,:] now contains [1,2,3]
If the reason you prefer the push!
approach is because you don't want to keep track of the index and pass it manually, then you can automate this process in your function too, by keeping a mutatable counter e.g.
function f(mat, c, x)
c[1] = c[1] + 1;
mat[c[1], :] = x;
end;
julia> mat = Array(Int64, (100,3)); counter = [0];
julia> f(mat, counter, [1 2 3]);
julia> f(mat, counter, [1 2 3]);
julia> f(mat, counter, [1 2 3]);
julia> mat[1:3,:]
3×3 Array{Int64,2}:
1 2 3
1 2 3
1 2 3
Alternatively, you can even create a closure, i.e. a function with state, that has an internal counter, and forget about keeping an external counter variable, e.g.
julia> f = () -> (); # creating an f at 'outer' scope
julia> let c = 1 # creates c at local 'let' scope
f = (mat, x) -> (mat[c,:] = x; c += 1;) # f reassigned from outer scope
end; # f now contains the 'closed' variable c
julia> mat = Array(Int64, (100,3));
julia> f(mat, [1 2 3]);
julia> f(mat, [2 3 4]);
julia> f(mat, [3 4 5]);
julia> mat[1:3,:]
3×3 Array{Int64,2}:
1 2 3
2 3 4
3 4 5
Upvotes: 0
Reputation: 12051
Multi-dimensional arrays cannot have their size changed. There are pointer hacks to share data, but these do not modify the size of the original array.
Even if it were possible, be aware that because Julia matrices are column major, this operation is very slow, and requires a copy of the array.
In Julia, operations that modify the data passed in (i.e., performing computations on data instead of with data) are typically marked with !
. This denotes to the programmer that the collection being processed will be modified. These kinds of operations are typically called "in-place" operations, because although they are harder to use and reason about, they avoid using additional memory, and can usually complete faster.
There is no way to avoid a copy for this operation because of how matrices are stored in memory. So there is not much real benefit to turning this particular operation into an in-place operation. Therefore, I recommend against it.
If you really need this operation for some reason, you should not use a matrix, but rather a vector of vectors:
v = Vector{Float64}[]
push!(v, [1.0, 2.0, 3.0])
This data structure is slightly slower to access, but much faster to add to.
On the other hand, from what it sounds like, you may be interested in a more specialized data structure, such as a DataFrame
.
Upvotes: 7