Reputation: 51
I have a three dimensional array defined as:
x=zeros(Float64,2,2,2)
I want to assign ones to x
by passing x
to a function, one layer at a time.
The function is:
function init(p,y)
y=ones(p,p)
end
and I will pass x
as follows:
for k=1:2
init(2,x[2,2,k])
end
but when I do that, x
is zeros, not ones. Why?
julia> x
2x2x2 Array{Float64,3}:
[:, :, 1] =
0.0 0.0
0.0 0.0
[:, :, 2] =
0.0 0.0
0.0 0.0
Any idea how to get Julia to assign ones to x
?
Upvotes: 5
Views: 1678
Reputation: 5083
A lot has changed in Julia, and I thought I would update this answer to reflect Julia 1.5 (probably most of the changes were 1.0). While I would expect the modern x[:, :, k]
to work, as this is still refered to as a SubArray
this actually is copy now when in an expression. Instead you must use view()
:
x= zeros(2, 2, 2)
function init!(y)
y[:]= ones(size(y))
end
init!(view(x, :, :, 1)) # get reference to original items
This gives you the desired result:
julia> x
2×2×2 Array{Float64,3}:
[:, :, 1] =
1.0 1.0
1.0 1.0
[:, :, 2] =
0.0 0.0
0.0 0.0
There are also helper macros for writing it in a more palatable form,
init!(@view x[:,:,1])
but you run the danger of greedy macro parsing if you have other arguments, such that
otherfunc!(@view x[:,:,1], 10)
would give you an error Invalid use of @view macro: argument must be a reference expression
. To get around this, there is the kludge @views
which turns all SubArrays into views, or you can wrap the argument in parenthesis.
otherfunc!(@views x[:,:,1], 10)
otherfunc!(@view( x[:,:,1]), 10)
You can find more information on the manipulation of Arrays and Matrices in this presentation: (Youtube) Arrays: slices and views
Upvotes: 0
Reputation: 5325
One possible solution is to use slice
, which makes a SubArray
:
x = zeros(2, 2, 2) # Float64 by default
function init!(y)
y[:] = ones(y) # change contents not binding
end
for k in 1:2
init!(slice(x, :, :, k)) # use slice to get SubArray
end
Note that you can use ones(y)
to get a vector of ones of the same size as y
.
A SubArray
gives a view of an array, instead of a copy. In future versions of Julia, indexing an array may give this by default, but currently you must do it explicitly.
For a discussion about values vs. bindings, see
http://www.johnmyleswhite.com/notebook/2014/09/06/values-vs-bindings-the-map-is-not-the-territory/
EDIT: I hadn't seen @tholy's answer, which contains the same idea.
Upvotes: 6
Reputation: 12179
I'm also not sure I understand the question, but slice(x, :, :, k)
will take a 2d slice of x
.
If you're initializing x
as an array of Float64
and then hoping to assign a matrix to each element (which is what it appears you're trying to do), that won't work---the type of x
won't allow it. You could make x
an array of Any
, and then that would be permitted.
Upvotes: 5
Reputation: 632
I'm not certain I understand, but if you're trying to modify x in place, you'll want to do things a little differently.
The code below should do what you need.
x = zeros(Float64, 2, 2, 2)
function init!(p, y, k)
y[:, :, k] = ones(Float64, p, p)
end
for k = 1:2
init!(2, x, k)
end
And you might also want to keep in mind that the standard convention in Julia is to include an exclamation mark in the name of a function that modifies its arguments. And if I've understood your question, then you want your init!()
function to do exactly that.
Upvotes: 3