I'm trying to populate an array of functions in Julia. A minimal example is
function nice()
i=1
while true
f() = i
produce(f)
i+=1
end
end
ye = Task(() -> nice())
funcs = Function[]
for i in [1:2]
push!(funcs,consume(ye))
println("Why does this not stay the same???")
println(funcs[1]())
end
The problem is that funcs[1] changes from the one that returns 1 to the one that returns 2! Please help me out!
This solved it. I needed a let command and and f=()->j statement
function nice()
i=1
while true
let j=i
f=()->j
produce(f)
end
i+=1
end
end
ye = Task(() -> nice())
funcs = Function[]
for k in [1:2]
push!(funcs,consume(ye))
end
println(funcs[1]())
println(funcs[2]())
Related
I have a function within which I would like to evaluate a list of expressions using interpolated literals passed as arguments to the function and assign the result to a new array. Previously I've been able to do this fairly simply:
array_of_expr = Any[:(A * 5), :(B * 6 * T)]
arglist = [:A; :B; :T]
test_input = [1e5, 1e1, 100]
#eval begin
function interpolate_all($(arglist...))
result_array = $(Expr(:vcat, array_of_expr...))
end
end
interpolate_all(test_input...)
which returns the expected result:
2-element Array{Float64,1}:
500000.0
6000.0
(I know the code seems needlessly complicated with the #eval--it's because in the full version of the code, arglist is ~500 items long. This has been causing some compiler errors in the full version of the code, so my attempts here to loop over array_of_expr are part of my testing to find out the exact error, and also help me better understand metaprogramming/variable scope while I'm at it.)
I can index array_of_expr and evaluate an individual elements manually in this MWE:
#eval begin
function interpolate_one($(arglist...))
result_array = similar(array_of_expr)
println("evaluating $(array_of_expr[2])")
result_array = $(array_of_expr[2])
println(result_array)
end
end
interpolate_one(test_input...)
which returns:
evaluating B * 6 * T
6000.0
Which is the expected behavior. But if I try to loop over array_of_expr, I encounter various errors. The following ignores the iterator i and just prints the symbolic expression:
#eval begin
function interpolate_1by1($(arglist...))
result_array = similar(array_of_expr)
# this doesn't work, it just gives the symbolic expression, basically ignoring the $:
for i in range(1, length=length(array_of_expr))
result_array[i] = ($array_of_expr[i])
end
println(result_array)
end
end
interpolate_1by1(test_input...)
The following reports that i is not defined, which I understand is because expressions are evaluated in the global scope, not local:
#eval begin
function interpolate_1by1($(arglist...))
result_array = similar(array_of_expr)
# this doesn't work, i is undefined:
for i in range(1, length=length(array_of_expr))
result_array[i] = $(array_of_expr[i])
end
end
end
interpolate_1by1(test_input...)
Is there any way to make this work? I have tried the tactics in the cited SE answer, but did not have success.
you may unroll the loop at compile time:
#eval begin
function interpolate_1by1($(arglist...))
result_array = similar($array_of_expr)
$((quote
result_array[$i] = $(array_of_expr[i])
end for i in 1:length(array_of_expr))...)
return result_array
end
end
which after expansion is similar to
function interpolate_1by1($(arglist...))
result_array = similar($array_of_expr)
result_array[1] = $(array_of_expr[1])
result_array[2] = $(array_of_expr[2])
...
return result_array
end
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
I have a function that optionally uses threads for its main loop, doing so when an argument usingthreads is true. At the moment, the code looks like this:
function dosomething(usingthreads::Bool)
n = 1000
if usingthreads
Threads.#threads for i = 1:n
#20 lines of code here
end
else
for i = 1:n
#same 20 lines of code repeated here
end
end
end
Less nasty than the above would be to put the "20 lines" in a separate function. Is there another way?
You could use a macro that changes its behavior depending on the result of Threads.nthreads():
macro maybe_threaded(ex)
if Threads.nthreads() == 1
return esc(ex)
else
return esc(:(Threads.#threads $ex))
end
end
Without threading, this macro will be a no-op:
julia> #macroexpand #maybe_threaded for i in 1:5
print(i)
end
:(for i = 1:5
#= REPL[2]:2 =#
print(i)
end)
But when threading is enabled and e.g. JULIA_NUM_THREADS=4 it will expand to the threaded version:
julia> #maybe_threaded for i in 1:5
print(i)
end
41325
Edit: Upon rereading the question, I realize this doesn't really answer it but it might be useful anyway.
You can use ThreadsX as suggested in this discourse link.
The answer from the thread (all credit to oxinabox):
using ThreadsX
function foo(multi_thread=true)
_foreach = multi_thread ? ThreadsX.foreach : Base.foreach
_foreach(1:10) do ii
#show ii
end
end
Can I add type information to arguments that are functions?
Consider the following example:
function f{T} (func, x::Int)
output = Dict{Int, Any}()
output[x] = func(x)
return output
end
I don't like that I have to say Any for the value type of the dictionary. I'd much rather do the following:
function f{T} (func::Function{Int->T}, x::Int)
output = Dict{Int, T}()
output[x] = func(x)
return output
end
Can I provide type hints of functions like this? I kind of want to say the following
f :: (Int -> T), Int -> Dict{Int, T}
Not currently. We may add something along those lines in the future, however.
This is not an answer to the main question, but more a really ugly workaround the Any in the Dict issue:
function f(func, x::Int)
T = code_typed(func, (Int,))[1].args[3].typ
output = Dict{Int, T}()
output[x] = func(x)
return output
end
That is probably not efficient and will probably work only on simple cases (which do not even include anonymous functions) like
>>> g(x) = x*2
>>> typeof(f(g, 1234))
Dict{Int64,Int64}
>>> h(x) = x > zero(x) ? x : nothing
>>> typeof(f(h, 1234))
Dict{Int64,Union(Int64,Nothing)}
EDIT:
This works better:
function f(func, x::Int)
[x => func(x)]
end
>>> dump( f(x->2x, 3) )
Dict{Int64,Int64} len 1
3: Int64 6
what is the prefered way to investigate or print out further detail (print input variable of a function, iteration number, etc.) of a failed #test inside a #testset?
I tried to wrap a try-catch-block around it. However, it doesn't seem to fire.
Here is a made-up example:
using Base.Test
rng = MersenneTwister(3231);
# define function that works different than expected
function compare(a,b)
if a == 3 && b == 3
return false
else
return a == b
end
end
# test function in a test set
#testset "Test Compare Function" begin
for iii = 1:10
number = rand(rng,1:10)
try
#test compare(number,number) == true
catch
#show(number)
end
end
end
Thank you very much!
You need to make sure it tests after the printing.
#testset "Test Compare Function" begin
for iii = 1:10
number = rand(rng,1:10)
#test begin
res = compare(number,number) == true
if !res
#show number
flush(STDOUT)
end
res
end
end
end