1
votes

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.

1

1 Answers

1
votes

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