Hello,

I found I use statements like this a lot:

```
A = func(A)
```

with

`func`

a function returning a array of the same size.

Is this efficient? Or there is a better way to write this kind of things?

Thanks.

Hello,

I found I use statements like this a lot:

```
A = func(A)
```

with

`func`

a function returning a array of the same size.

Is this efficient? Or there is a better way to write this kind of things?

Thanks.

2 Likes

if type of `A`

changed, please use `let A = func(A)`

to *update symbol* for better performance.

Also, if original A is not used any more and you want a same-length and same-type array with specific initial value, use `fill!`

and no need to assign here.

There is no need for this anymore.

It is just as effective as `B = func(A)`

. If you want to reuse the memory of `A`

you need to update it in place, in Juila functions like that conventionally end with an exclamation mark, e.g.

```
function func!(A)
# modify A
end
A = ... # initialize
func!(A)
func!(A) # reuse memory of A
```

5 Likes

Incredible. If it also applys when `A`

is a free variable shared to others, I think this optimization is very advanced and cool.

1 Like

Thank you.

I use Python to do numerical computation.

Trying to learn Julia, I found some basics are quite different.

So this may be stupid question.

If I define a function like

```
function func!(a)
# change a
end
```

and call `func!(A)`

, then if `A`

is an array, the values in `A`

get changed.

But if `A`

is just a variable, the value is not changed.

Is that right?

1 Like

First, thereâ€™s no distinction between A â€śis an arrayâ€ť vs A â€śis just a variableâ€ť.

If you call `func!(A)`

and you mutate `A`

, the change will be reflected in the `A`

in the caller.

If you call `func!(A)`

and you assigned to `A`

in `func!`

, the change will not be reflected in the caller.

This is exactly the same as python.

3 Likes

Hereâ€™s an example showing how Juliaâ€™s behavior is exactly the same as Pythonâ€™s when it comes to assignment vs. mutation:

Python:

```
In [1]: def f(x):
...: x = [1, 2, 3]
...:
In [2]: x = [0, 0, 0]
In [3]: f(x)
In [4]: x
Out[4]: [0, 0, 0]
In [5]: def g(x):
...: x[0] = 1
...: x[1] = 2
...: x[2] = 3
...:
In [6]: g(x)
In [7]: x
Out[7]: [1, 2, 3]
```

Julia:

```
julia> function f(x)
x = [1, 2, 3]
end
f (generic function with 1 method)
julia> x = [0, 0, 0]
3-element Array{Int64,1}:
0
0
0
julia> f(x)
3-element Array{Int64,1}:
1
2
3
julia> x
3-element Array{Int64,1}:
0
0
0
julia> function g(x)
x[1] = 1
x[2] = 2
x[3] = 3
end
g (generic function with 1 method)
julia> g(x)
3
julia> x
3-element Array{Int64,1}:
1
2
3
```

In both languages, the function `f(x)`

assigns a new value to the name `x`

within the function. This has no effect on the value `x`

which was passed in. The function `g(x)`

, on the other hand, actually mutates the value, and we can see that mutation in the value `x`

which was passed in.

As @yuyichao said, this has nothing to do with the fact that `x`

is an array, and is only affected by the difference between assigning a new value vs. mutating an existing value.

5 Likes

The above replies by @yuyichao and @rdeits have already given you a good explanation of what happens in each case. Here is a concrete example to answer your question directly.

```
julia> function func(A)
B = sin.(A ./ 3)
end
func (generic function with 1 method)
julia> function func!(A)
@. A = sin(A / 3)
end
func! (generic function with 1 method)
julia> using BenchmarkTools
julia> @btime func($A);
5.428 ms (2 allocations: 7.63 MiB)
julia> @btime func!($A);
2.761 ms (0 allocations: 0 bytes)
```

1 Like

Thank you for all the clarification.

Now I know the difference between assignment and mutation.

Can I ask what `@. A = sin(A / 3)`

means?

Thank you.

`@.`

is a macro which replaces every function with an element-wise version of the function. So this replaces every element of the stay with the sin of 1/3rd of the element.

2 Likes

Is it equivalent to `A .= sin.(A ./ 3)`

and thus is a mutation instead of assignment?

1 Like

Yes

1 Like

If you ever wonder what a macro is doing, two approaches can help.

The first is typing `?`

in the repl, to enter help mode.

```
help?> @.
@. expr
Convert every function call or operator in expr into a "dot call" (e.g. convert f(x) to f.(x)), and convert every assignment in expr to a "dot assignment" (e.g. convert += to .+=).
If you want to avoid adding dots for selected function calls in expr, splice those function calls in with $. For example, @. sqrt(abs($sort(x))) is equivalent to sqrt.(abs.(sort(x))) (no dot for sort).
(@. is equivalent to a call to @__dot__.)
Examples
â‰ˇâ‰ˇâ‰ˇâ‰ˇâ‰ˇâ‰ˇâ‰ˇâ‰ˇâ‰ˇâ‰ˇ
julia> x = 1.0:3.0; y = similar(x);
julia> @. y = x + 3 * sin(x)
3-element Array{Float64,1}:
3.5244129544236893
4.727892280477045
3.4233600241796016
```

This works for anything that someone wrote documentation for. That includes most things in Base Julia and many libraries (including the standard libraries). For example:

```
julia> using LinearAlgebra
help?> mul!
search: mul! rmul! lmul! accumulate! muladd vmul vmuladd widemul accumulate module Module mutable struct @__MODULE__ baremodule parentmodule NamedTuple isimmutable promote_rule SegmentationFault
mul!(Y, A, B) -> Y
Calculates the matrix-matrix or matrix-vector product AB and stores the result in Y, overwriting the existing value of Y. Note that Y must not be aliased with either A or B.
```

For macros specifically, there is also the helpful macro `@macroexpand`

:

```
julia> @macroexpand @. A = sin(A / 3)
:(A .= sin.((/).(A, 3)))
```

Which expands any macros so you can see what theyâ€™re doing to the code. Itâ€™ll expand all macros in the expression that follows.

```
julia> @macroexpand @time @. A = sin(A / 3)
quote
#= util.jl:158 =#
local var"#7#stats" = Base.gc_num()
#= util.jl:159 =#
local var"#9#elapsedtime" = Base.time_ns()
#= util.jl:160 =#
local var"#8#val" = (A .= sin.((/).(A, 3)))
#= util.jl:161 =#
var"#9#elapsedtime" = Base.time_ns() - var"#9#elapsedtime"
#= util.jl:162 =#
local var"#10#diff" = Base.GC_Diff(Base.gc_num(), var"#7#stats")
#= util.jl:163 =#
Base.time_print(var"#9#elapsedtime", (var"#10#diff").allocd, (var"#10#diff").total_time, Base.gc_alloc_count(var"#10#diff"))
#= util.jl:165 =#
Base.println()
#= util.jl:166 =#
var"#8#val"
end
```

In case you find this hard to read, the library `MacroTools`

has (among many other useful tools) the function `prettify`

, which makes them much more readable:

```
julia> using MacroTools
julia> prettify(@macroexpand @time @. A = sin(A / 3))
quote
local tapir = Base.gc_num()
local camel = Base.time_ns()
local guanaco = (A .= sin.((/).(A, 3)))
camel = Base.time_ns() - camel
local hippopotamus = Base.GC_Diff(Base.gc_num(), tapir)
Base.time_print(camel, hippopotamus.allocd, hippopotamus.total_time, Base.gc_alloc_count(hippopotamus))
Base.println()
guanaco
end
```

12 Likes

Thanks for all replies. They are very helpful.

Julia has an excellent community.

2 Likes