I have a function
function foo(a)
if a > 5
a = 5
end
some_more_code
end
If the if-statement is true I would like to end the function but I don't want to return anything - to change the value of a is all I need.
How do I do that?
You can write (note that I have also changed the syntax of function definition to make it more standard for Julia style):
function foo(a)
if a > 5
a = 5
return
end
# some_more_code
end
Just use the return keyword without any expression following it. To be precise in such cases Julia returns nothing value of type Nothing from a function (which is not printed in REPL and serves to signal that you did not want to return anything from a function).
Note though that the value of a will be only changed locally (within the scope of the function), so that outside of the function it will be unchanged:
julia> function foo(a)
if a > 5
a = 5
return
end
# some_more_code
end
foo (generic function with 1 method)
julia> x = 10
julia> foo(x)
julia> x
10
In order to make the change visible outside of the function you have to make a to be some kind of container. A typical container for such cases is Ref:
julia> function foo2(a)
if a[] > 5
a[] = 5
return
end
# some_more_code
end
foo2 (generic function with 1 method)
julia> x = Ref(10)
Base.RefValue{Int64}(10)
julia> foo2(x)
julia> x[]
5
Related
What is the most idiomatic way to achieve function currying?
Eg. in Haskell:
times a b = a * b
-- This can then be used uncurried:
times 2 3 -- Result is 6
-- But there is also auto-currying:
(times 2) 3 -- This works too
In Julia, some built-ins support this:
<(8, 7) # Result is false
<(8)(7) # Same
7 |> <(8) # Same
However, user-defined functions don't automatically have this functionality:
times(a, b) = a * b
times(2, 3) # Result is 6
3 |> times(2) # MethodError: no method matching times(::Int64)
I can manually define a one-argument version and then it works:
times(a) = b -> a * b
But my question is, is there a better way?
why not use curry.jl
times(a, b) = a * b
times_curry = curry(times)
times_curry(5)(2) ---> gives 10
This would be pretty tricky (impossible?) to solve in Julia without a macro, as multiple dispatch means you won't know which function you'll ultimately end up dispatching to until you see all of the arguments. I think the simplest way to implement currying is just with a struct like this:
julia> struct Curry
func::Function
args::Tuple
Curry(func, args...) = new(func, args)
end
julia> (curry::Curry)(x, args...) = Curry(curry.func, curry.args..., x, args...)
julia> (curry::Curry)() = curry.func(curry.args...)
julia> Curry(<)
Curry(<, ())
julia> Curry(<)(2)()
(::Base.Fix2{typeof(<), Int64}) (generic function with 1 method)
julia> Curry(<)(2)(3)()
true
julia> Curry(<, 2)
Curry(<, (2,))
julia> Curry(<, 2, 3)()
true
Basically, calling the Curry with at least one argument creates a new Curry with the new arguments, and calling it with 0 arguments “executes” the whole thing.
I'm trying to build a function that will output an expression to be assigned to a new in-memory function. I might be misinterpreting the capability of metaprogramming but, I'm trying to build a function that generates a math series and assigns it to a function such as:
main.jl
function series(iter)
S = ""
for i in 1:iter
a = "x^$i + "
S = S*a
end
return chop(S, tail=3)
end
So, this will build the pattern and I'm temporarily working with it in the repl:
julia> a = Meta.parse(series(4))
:(x ^ 1 + x ^ 2 + x ^ 3 + x ^ 4)
julia> f =eval(Meta.parse(series(4)))
120
julia> f(x) =eval(Meta.parse(series(4)))
ERROR: cannot define function f; it already has a value
Obviously eval isn't what I'm looking for in this case but, is there another function I can use? Or, is this just not a viable way to accomplish the task in Julia?
The actual error you get has to do nothing with metaprogramming, but with the fact that you are reassigning f, which was assigned a value before:
julia> f = 10
10
julia> f(x) = x + 1
ERROR: cannot define function f; it already has a value
Stacktrace:
[1] top-level scope at none:0
[2] top-level scope at REPL[2]:1
It just doesn't like that. Call either of those variables differently.
Now to the conceptual problem. First, what you do here is not "proper" metaprogramming in Julia: why deal with strings and parsing at all? You can work directly on expressions:
julia> function series(N)
S = Expr(:call, :+)
for i in 1:N
push!(S.args, :(x ^ $i))
end
return S
end
series (generic function with 1 method)
julia> series(3)
:(x ^ 1 + x ^ 2 + x ^ 3)
This makes use of the fact that + belongs to the class of expressions that are automatically collected in repeated applications.
Second, you don't call eval at the appropriate place. I assume you meant to say "give me the function of x, with the body being what series(4) returns". Now, while the following works:
julia> f3(x) = eval(series(4))
f3 (generic function with 1 method)
julia> f3(2)
30
it is not ideal, as you newly compile the body every time the function is called. If you do something like that, it is preferred to expand the code once into the body at function definition:
julia> #eval f2(x) = $(series(4))
f2 (generic function with 1 method)
julia> f2(2)
30
You just need to be careful with hygiene here. All depends on the fact that you know that the generated body is formulated in terms of x, and the function argument matches that. In my opinion, the most Julian way of implementing your idea is through a macro:
julia> macro series(N::Int, x)
S = Expr(:call, :+)
for i in 1:N
push!(S.args, :($x ^ $i))
end
return S
end
#series (macro with 1 method)
julia> #macroexpand #series(4, 2)
:(2 ^ 1 + 2 ^ 2 + 2 ^ 3 + 2 ^ 4)
julia> #series(4, 2)
30
No free variables remaining in the output.
Finally, as has been noted in the comments, there's a function (and corresponding macro) evalpoly in Base which generalizes your use case. Note that this function does not use code generation -- it uses a well-designed generated function, which in combination with the optimizations results in code that is usually equal to the macro-generated code.
Another elegant option would be to use the multiple-dispatch mechanism of Julia and dispatch the generated code on type rather than value.
#generated function series2(p::Val{N}, x) where N
S = Expr(:call, :+)
for i in 1:N
push!(S.args, :(x ^ $i))
end
return S
end
Usage
julia> series2(Val(20), 150.5)
3.5778761722367333e43
julia> series2(Val{20}(), 150.5)
3.5778761722367333e43
This task can be accomplished with comprehensions. I need to RTFM...
https://docs.julialang.org/en/v1/manual/arrays/#Generator-Expressions
The code at the end of this post constructs a function which is bound to the variables of a given dictionary. Furthermore, the function is not bound to the actual name of the dictionary (as I use the Ref() statement).
An example:
julia> D = Dict(:x => 4, :y => 5)
julia> f= #mymacro4(x+2y, D)
julia> f()
14
julia> DD = D
julia> D = nothing
julia> f()
14
julia> DD[:x] = 12
julia> f()
22
Now I want to be able to construct exactly the same function when I only have access to the expression expr = :(x+2y).
How do I do this? I tried several things, but was not able to find a solution.
julia> f = #mymacro4(:(x+2y), D)
julia> f() ### the function evaluation should also yield 14. But it yields:
:(DR.x[:x] + 2 * DR.x[:y])
(I actually want to use it within another macro in which the dictionary is automatically created. I want to store this dictionary and the function within a struct, such that I'm able to call this function at a later point in time and manipulate the objects in the dictionary. If necessary, I may post the complete example and explain the complete problem.)
_freevars2(literal) = literal
function _freevars2(s::Symbol)
try
if typeof(eval(s)) <: Function
return s
else
return Meta.parse("DR.x[:$s]")
end
catch
return Meta.parse("DR.x[:$s]")
end
end
function _freevars2(expr::Expr)
for (it, s) in enumerate(expr.args)
expr.args[it] = _freevars2(s)
end
return expr
end
macro mymacro4(expr, D)
expr2 = _freevars2(expr)
quote
let DR = Ref($(esc(D)))
function mysym()
$expr2
end
end
end
end
What is wrong with my code? Do I have to declare x before using it?
function f(n::Int64, t::Int64)
A = ones(n,n)
for i=0:t
if i > 0
A[x,a] = rand()*A[x,a] + rand()
end
y = rand(1:n)
b = rand(1:n)
if i > 0
A[x,a] = rand()*A[x,a] + rand()*A[y,b]
end
x = y
a = min(b, rand(1:n))
end
return A
end
Here is the error thrown when trying to call f:
UndefVarError: x not defined
I think that the reason is more complex, as similar code in Python would work.
For example compare (Python):
>>> def f():
... for i in range(3):
... if i > 0:
... print(a)
... a = i
...
>>> f()
0
1
to (Julia):
julia> function f()
for i in 0:2
if i > 0
println(a)
end
a = i
end
end
f (generic function with 1 method)
julia> f()
ERROR: UndefVarError: a not defined
So what is the difference? As the Julia manual explains here you have:
for loops, while loops, and comprehensions have the following behavior: any new variables introduced in their body scopes are freshly allocated for each loop iteration, as if the loop body were surrounded by a let block
This means that in your code variables a and x as they are local to the for loop are freshly allocated in each iteration of the loop. Because of this the variable has to be assigned to before it is accessed inside the loop.
Therefore it is not needed to assign a value to x and a before the loop. It is enough to define them in scope outer to the loop (even without assigning of the value). For example like this:
julia> function f(n::Int64, t::Int64)
A = ones(n,n)
local x, a
for i=0:t
if i > 0
A[x,a] = rand()*A[x,a] + rand()
end
y = rand(1:n)
b = rand(1:n)
if i > 0
A[x,a] = rand()*A[x,a] + rand()*A[y,b]
end
x = y
a = min(b, rand(1:n))
end
return A
end
f (generic function with 1 method)
julia> f(1,1)
1×1 Array{Float64,2}:
0.94526289614139
Now it works because x and a are not freshly allocated in each iteration of the loop.
In my original toy example it would look like:
julia> function f()
local a
for i in 0:2
if i > 0
println(a)
end
a = i
end
end
f (generic function with 2 methods)
julia> f()
0
1
and you see that you get exactly what you had in Python.
I would like to be able to create a dispatch for a user-defined type which will essentially do an inplace copy. However, I would like to do it in a type-stable manner, and thus I would like to avoid using getfield directly, and instead try to use a generated function. Is it possible for a type like
type UserType{T}
x::Vector{T}
y::Vector{T}
z::T
end
to generate some function
recursivecopy!(A::UserType,B::UserType)
# Do it for x
if typeof(A.x) <: AbstractArray
recursivecopy!(A.x,B.x)
else
A.x = B.x
end
# Now for y
if typeof(A.y) <: AbstractArray
recursivecopy!(A.y,B.y)
else
A.y = B.y
end
# Now for z
if typeof(A.z) <: AbstractArray
recursivecopy!(A.z,B.z)
else
A.z = B.z
end
end
The recursivecopy! in RecursiveArrayTools.jl makes this handle nested (Vector{Vector}) types well, but the only problem is that I do not know the fields the user will have in advance, just at compile-time when this function would be called. Sounds like a job for generated functions, but I'm not quite sure how to generate this.
You don't need to bend over backwards to avoid getfield and setfield. Julia can infer them just fine. The trouble comes when Julia can't figure out which field it's accessing… like in a for loop.
So the only special thing the generated function needs to do is effectively unroll the loop with constant values spliced into getfield:
julia> immutable A
x::Int
y::Float64
end
julia> #generated function f(x)
args = [:(getfield(x, $i)) for i=1:nfields(x)]
:(tuple($(args...)))
end
f (generic function with 1 method)
julia> f(A(1,2.4))
(1,2.4)
julia> #code_warntype f(A(1,2.4))
Variables:
#self#::#f
x::A
Body:
begin # line 2:
return (Main.tuple)((Main.getfield)(x::A,1)::Int64,(Main.getfield)(x::A,2)::Float64)::Tuple{Int64,Float64}
end::Tuple{Int64,Float64}
Just like you can splice in multiple arguments to a function call, you can also directly splice in multiple expressions to the function body.
julia> type B
x::Int
y::Float64
end
julia> #generated function f!{T}(dest::T, src::T)
assignments = [:(setfield!(dest, $i, getfield(src, $i))) for i=1:nfields(T)]
:($(assignments...); dest)
end
f! (generic function with 1 method)
julia> f!(B(0,0), B(1, 2.4))
B(1,2.4)
julia> #code_warntype f!(B(0,0), B(1, 2.4))
Variables:
#self#::#f!
dest::B
src::B
Body:
begin # line 2:
(Main.setfield!)(dest::B,1,(Main.getfield)(src::B,1)::Int64)::Int64
(Main.setfield!)(dest::B,2,(Main.getfield)(src::B,2)::Float64)::Float64
return dest::B
end::B
You can, of course, make the body of that comprehension as complicated as you'd like. That effectively becomes the inside of your for loop. Splatting the array into the body of the function does the unrolling for you.