2

I'm trying to run a loop over different functions with different number of arguments. The variables are created at runtime inside the loop, and I want to use eval at each iteration to instantiate a Struct using the variable :symbol. However, I can't do this since eval only works in the global scope. This is the MWE for the case that works:

function f1(x); return x; end
function f2(x1,x2); return x1+x2; end

handles = [f1,f2]
args =[:(x1),:(x1,x2)]

x1 = 1; x2 = 1;
for (i,f) in enumerate(handles)
    params = eval(args[i])
    @show f(params...)
end

f(params...) = 1
f(params...) = 2

However, if I move the variable definitions inside the loop, which is what I actually want, it doesn't work after restarting Julia to clear the workspace.

function f1(x); return x; end
function f2(x1,x2); return x1+x2; end

handles = [f1,f2]
args =[:(x1),:(x1,x2)]

for (i,f) in enumerate(handles)
    x1 = 1; x2 = 1;
    params = eval(args[i])
    @show f(params...)
end

ERROR: UndefVarError: x1 not defined

I've tried several of the answers, such as this one, but I can't seem to make it work. I could write a custom dispatch function that takes[x1,x2] and calls f1 or f2 with the correct arguments. But still, is there any way to do this with eval or with an alternative elegant solution?

EDIT: here are more details as to what I'm trying to do in my code. I have a config struct for each algorithm, and in this I want to define beforehand the arguments it takes

KMF_config = AlgConfig( 
    name = "KMF",
    constructor = KMC.KMF,
    parameters = :(mu,N,L,p),
    fit = KMC.fit!)
MF_config = AlgConfig( 
    name = "MF",
    constructor = KMC.MF,
    parameters = :(mu,N,L),
    fit = KMC.fit!)

alg_config_list = [KMF_config, MF_config]
for (i,alg_config) in enumerate(alg_config_list)
    mu,N,L,p,A,B,C,D,data = gen_vars() #this returns a bunch of variables that are used in different algorithms
    method = alg_config.constructor(eval(method.parameters)...)
    method.fit(data)
end

One possible solution is to have a function take all the variables and method, and return a tuple with a subset of variables according to method.name. But I'm not sure if it's the best way to do it.

Pedro G.
  • 395
  • 1
  • 2
  • 10
  • can you show an example of the struct generation in the case that works? maybe an alternative solution can be done without the use of `eval` – longemen3000 Dec 24 '19 at 22:30
  • It doesn't really work since my variables are not global, but I've added an example with what I'm actually trying to do. – Pedro G. Dec 24 '19 at 23:06

1 Answers1

2

Here's an approach using multiple dispatch rather than eval:

run_a(x, y) = x + 10*y
run_b(x, y, z) = x + 10*y + 100*z

extract(p, ::typeof(run_a)) = (p.x, p.y)
extract(p, ::typeof(run_b)) = (p.x, p.y, p.z)
genvars() = (x=1, y=2, z=3)

function doall()
    todo = [
        run_a,
        run_b,
    ]
    for runalg in todo
        v = genvars()
        p = extract(v, runalg)
        @show runalg(p...)
    end
end

In your example you would replace run_a and run_b with KMC.KMF and KMC.MF.

Edit: Cleaned up example to avoid structs that don't exist in your example.

Carl Morris
  • 156
  • 3
  • 5