How do you define a mutating function in Julia, where you want the result to be written to one of it's inputs.
I know functions exist like push!(list, a)
, which mutate their inputs but how can I define one of my own using the exclamation mark.
Mutating input parameters inside a function is a very frequent bad practice, and dangerous because it's made without awareness of the consequence, especially in multithread application.
an exclamation mark is a prefix operator for logical negation ("not") a! function names that end with an exclamation mark modify one or more of their arguments by convention.
00:07 In the last lesson, you learned that a Python function can't change the value of an argument by reassigning the corresponding parameter to something else. However, Python can mutate the object passed as an argument if the object itself, the object that's passed as an argument, can itself be mutated.
The !
is just a convention; it is not a requirement for mutating functions.
Any function can mutate its inputs.
But so that it is clear that it is doing so, we suffix it with a !
.
The input to be mutated does have to be mutable, though.
Which excludes String
s, Tuples, Int64
s, Float32
s etc.
As well as custom types defined without the mutable
keyword.
Many types are mutable, like Vectors. But you do need to be sure to be changing their contents, rather than the references to them.
Say, for example, we want to make a function that replaces all elements of a vector with the number 2.
(fill!(v,2)
is the Base method to do this. But for example's sake)
Changing what v
contains:
function right1_fill_with_twos!(v::Vector{Int64})
v[:]=[2 for ii in 1:length(v)]
end
Which is the same as:
function right2_fill_with_twos!(v::Vector{Int64})
for ii in 1:length(v)
v[ii]=2
end
v
end
is changing thing what that name v
points to
function wrong1_fill_with_twos!(v::Vector{Int64})
v=[2 for ii in 1:length(v)]
end
Which is the same as:
function wrong2_fill_with_twos!(v::Vector{Int64})
u = Vector{Int64}(length(v))
for ii in 1:length(v)
u[ii]=2
end
v = u
end
The reason you cannot modify immutable variables (like Int64
s),
is because they don't have contents to modify -- they are their own contents.
This notion that you must change the Content of a variable passed in to a function, rather than change what the name is bound to (replacing the object) is a fairly standard thing in many programming languages. It comes from pass by value, where some values are references. I've heard it called the Golden Rule (of References) in Java
It is possible to assign new values to a constant within a function if you're willing to feed that function a symbol corresponding to the constant as an argument, although I think that some might argue that this doesn't satisfy Julia programming best practices:
function modify_constant!(constant_symbol::Symbol, other_arg)
new_val = eval(constant_symbol) + other_arg
eval(Main, Expr(:(=), constant_symbol, new_val))
end
y = 2
modify_constant!(:y, 3)
julia> y
5
Or, if one wanted, a bit more concisely:
function modify_constant!(constant_symbol::Symbol, other_arg)
eval(Expr(:(+=), constant_symbol, new_val))
end
For more discussion of a related issue, see this Github discussion
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With