Amal
Amal

Reputation: 51

Passing two-dimensional array to a function in julia

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

Answers (4)

woolstar
woolstar

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

David P. Sanders
David P. Sanders

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

tholy
tholy

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

paulstey
paulstey

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

Related Questions