I see codes like this
x.= x./maximum(abs.(x))
Here x is a vector. Personally, I would write
x = x./maximum(abs.(x))
Is there any significant difference between the two?
I see codes like this
x.= x./maximum(abs.(x))
Here x is a vector. Personally, I would write
x = x./maximum(abs.(x))
Is there any significant difference between the two?
First one is broadcasted/elementwise assignment, which mutates the instance assigned to x
, second one reassigns x
entirely. You can use Meta.@lower
to see how the expressions are lowered, and it tells you a bit of what is happening.
Before that, you should read a pretty good explanation of dots here:
And this gets into improved design and some of the implementation, though if you donβt have to care too much about it, you could just take away Meta.@lower
for seeing the broadcast loops, and the fact that broadcasting can be customized away from the default of instantiating a full Array
when some types allow for it, like elementwise addition of ranges.
Just to make it very clear to future readers that are novices in the language:
They are two completely different things.
The first ( x.= ...
) changes each element of the already existing object denoted by x
one-by-one with what is being computed. If you use multiple elements of x
to compute each new value in x
and you need to compute the new values solely based in the old values (without the new ones mixed in) then you may end up with bugs.
The second (x = ...
) just creates a completely new object and starts using the name x
for it.
EDIT: as others pointed out, if both are correct for you (i.e., there is no problem in overwriting the object denoted by x
during the computation) then x .= ...
is often preferable because it allocates less memory (reuses the existing object) but I would urge a novice to not care about premature optimization in this context (do it only if it is safe AND memory allocation shows to be a bottleneck).
Itβs like altering your laptop to add the features that you want, versus buying a new model with those features inbuilt. Both get you the same result, but one is significantly more resource intensive than the other.
julia> Meta.@lower x = f1.(x)
:($(Expr(:thunk, CodeInfo(
@ none within `top-level scope`
1 β %1 = Base.broadcasted(f1, x)
β %2 = Base.materialize(%1)
β %3 = Core.get_binding_type(Main, :x)
β %4 = Base.convert(%3, %2)
β %5 = Core.typeassert(%4, %3)
β x = %5
βββ return %2
))))
julia> Meta.@lower x .= f1.(x)
:($(Expr(:thunk, CodeInfo(
@ none within `top-level scope`
1 β %1 = x
β %2 = Base.broadcasted(f1, x)
β %3 = Base.materialize!(%1, %2)
βββ return %3
))))
More dots arenβt always better. Keep then one in .=
, but change the call to maximum like this:
x .= x ./ maximum(abs, x)
maximum(abs.(x))
will first create a temporary array and then find the max of that array, while maximum(abs, x)
does not.
Could also be reduced to a nice single dot :
x ./= maximum(abs, x)