A particular function that modifies the input

I think I’m going to learn a lot whith this question.

First, my principal goal is to transform any matrix according the following rule:
For each column, find the first element equal to 1.1. Suposse the position of this elememt is [n,j]. So, replaces all elemnts [n:end,j] with [n-1,j].
My scripts for this is the following:

function tabela_taxas(tab)
    for j in 1:size(tab)[2]
        aux = tab[:,j]
        i = 1
        while aux[i] != 1.1
            i = i+1
        end
        element_aux = aux[i-1]
        tab[i:end,j] .= element_aux
    end
    return tab
end

My first question, but not the most important, is that I think there is other way (more elegant) to writte the tabela_taxas function.

The second question is related to something strange. First, I will run the tabela_taxas function. For this, I’m going to simulate a specific matrix that allows me to test my function

tabela = rand(6,5)
tabela[3,1] = 1.1
tabela[2,2] = 1.1
tabela[5,3] = 1.1
tabela[2,4] = 1.1
tabela[2,5] = 1.1

Now, using my function:

tabela2 = tabela_taxas(tabela)

You can see that the original matrix, tabela, has been modified. Which shouldn’t happen because the matrix tabela is an input and not an output. It’s as if I had done:

tabela = tabela_taxas(tabela)

Can someone explain?

1 Like

The input matrix is the output matrix – notice how your functions modifies and returns the input tab, without reassigning to it. If you don’t want to do this, you can add tab = copy(tab) to your function before modifying tab.
You can confirm that they’re the same matrix via tabela === tabela2.

For example:

function tabela_taxas!(tab) # mutating definition
    for j in axes(tab, 2)
        aux = @view(tab[:,j])
        i = findfirst(==(1.1), aux)
        i === nothing || (aux[i:end] .= aux[i-1])
    end
    return tab
end

# non-mutating
tabela_taxas(tab) = tabela_taxas!(copy(tab))
4 Likes

In Julia docs for array:

In Julia, all arguments to functions are passed by sharing (i.e. by pointers).

You can read the Wikipedia section to understand passing by sharing. Happy learning!

3 Likes

A pretty common pattern is to write two functions:

myfun(x) = myfun!(copy(x))

function myfun!(x) 
    x[3] = 5 # or some other mutating code
    return x
end
5 Likes

I’m not sure this is elegant, and won’t be as fast as @Elrod’s one above, but you can write many such things with accumulate:

julia> tabela
6×5 Matrix{Float64}:
 0.0653491  0.982055  0.419218  0.242059  0.168234
 0.149178   1.1       0.398028  1.1       1.1
 1.1        0.190122  0.678386  0.854605  0.0642094
 0.763535   0.433054  0.278625  0.433437  0.231305
 0.0410386  0.528489  1.1       0.229503  0.779562
 0.757617   0.383551  0.806496  0.151491  0.782904

julia> accumulate(tabela; dims=1, init=(false, NaN)) do (flag,val),x
        (flag || x==1.1) ? (true,val) : (false,x)
       end .|> last
6×5 Matrix{Float64}:
 0.0653491  0.982055  0.419218  0.242059  0.168234
 0.149178   0.982055  0.398028  0.242059  0.168234
 0.149178   0.982055  0.678386  0.242059  0.168234
 0.149178   0.982055  0.278625  0.242059  0.168234
 0.149178   0.982055  0.278625  0.242059  0.168234
 0.149178   0.982055  0.278625  0.242059  0.168234
1 Like