使用 for 循环将文字插入数组中的每个表达式

Interpolating literals into each expression in an array using a for loop

我有一个函数,我想在其中使用作为参数传递给函数的内插文字来评估表达式列表,并将结果分配给新数组。以前我已经能够相当简单地做到这一点:

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...)

哪个returns预期结果:

2-element Array{Float64,1}:
 500000.0
 6000.0

(我知道 @eval 的代码似乎不必要地复杂——这是因为在完整版本的代码中,arglist 长约 500 项。这导致了一些编译器错误在完整版本的代码中,所以我在这里尝试循环 array_of_expr 是我测试的一部分,以找出确切的错误,同时也帮助我更好地理解 metaprogramming/variable 范围.)

我可以索引 array_of_expr 并在此 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...)

哪个returns:

evaluating B * 6 * T
6000.0

这是预期的行为。但是如果我尝试遍历 array_of_expr,我会遇到各种错误。以下忽略迭代器 i 并只打印符号表达式:

@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...)

下面的报告说i没有定义,我理解是因为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...)

有什么方法可以让它工作吗?我已经尝试了引用的 SE 答案中的策略,但没有成功。

您可以在编译时展开循环:

@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

扩展后类似于

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