4

考虑以下函数:

function func(vars::Dict{Symbol, Any})

end

我想在 afunc的范围内创建局部变量,其中:变量名是 a 的每个键,vars变量值是 avars中与给定键对应的值。

我知道我可以使用以下内容eval()

for (k, v) in vars
    eval(:($k = $v))
end

但是,这将在全局范围内定义变量,这会对性能产生影响并且可能会覆盖现有的全局变量。

PS 我将添加一个上下文,以防您有与我所采用的方法截然不同的建议。我正在围绕JuMP实现一个包装器,允许用户使用任意约束表达式动态创建优化模型。由于约束表达式是使用符号定义的,并且JuMP需要在当前范围内定义这些符号,因此我希望用户在字典中将这些符号及其值提供给函数。全局定义符号会很麻烦,因为理想情况下用户应该能够使用相同的约束表达式(即相同的符号)但不同的值多次运行该函数。

4

1 回答 1

2

注意:我不是元编程专家!

认为你可以做这样的事情,而不是使用通用函数,而是使用宏或生成的函数。

基于此:

julia> macro bar(dict)
           dump(dict, 10)
       end

julia> @bar Dict(:foo => "foo", :bar => "bar", :baz => "baz");
Expr 
  head: Symbol call
  args: Array(Any,(4,))
    1: Symbol Dict
    2: Expr 
      head: Symbol =>
      args: Array(Any,(2,))
        1: Expr 
          head: Symbol quote
          args: Array(Any,(1,))
            1: Symbol foo
          typ: Any
        2: ASCIIString "foo"
      typ: Any
    3: Expr 
      head: Symbol =>
      args: Array(Any,(2,))
        1: Expr 
          head: Symbol quote
          args: Array(Any,(1,))
            1: Symbol bar
          typ: Any
        2: ASCIIString "bar"
      typ: Any
    4: Expr
      head: Symbol =>
      args: Array(Any,(2,))
        1: Expr
          head: Symbol quote
          args: Array(Any,(1,))
            1: Symbol baz
          typ: Any
        2: ASCIIString "baz"
      typ: Any
  typ: Any

我能够做这样的事情,它可以做更多的错误检查,而且通用宏会在开发分支中使这更容易(至少对于错误检查):

julia> macro foo(dict)
           blk = Expr(:block)
           if dict.head == :call && dict.args[1] == :Dict
               for arg in dict.args[2:end]
                   sym = arg.args[1].args[1]
                   val = arg.args[2]
                   push!(blk.args, esc(:($sym = $val)))
               end
           else
               error("Need a Dict{Symbol, Any}")
           end
           blk
       end

julia> @foo Dict(:foo => "foo", :bar => "bar", :baz => "baz");

julia> @show foo bar baz;
foo = "foo"
bar = "bar"
baz = "baz"

julia> let    # local scope
           @foo Dict(:foo => "foo", :bar => "bar", :baz => "baz")
           @show foo bar baz
       end;
foo = "foo"
bar = "bar"
baz = "baz"

请注意,您不能创建一个 dict 变量,然后将其传递给@foo宏(同样,泛型宏可以使这更容易,因为可能有一种方法 for Symbolalso 并调度错误?):

julia> dict = Dict(:foo => "foo", :bar => "bar", :baz => "baz");

julia> @foo dict
ERROR: type Symbol has no field head

此时类似于:

@foo a 1 b 2 c 3

或者

@foo :a=>1 :b=>2 :c=>3

恕我直言会更好。

如果有人向我解释为什么这不是最佳的,我将不胜感激!这个答案应该是一个评论,但它有很多代码。

我将尝试使用通用宏和生成的函数来实现类似的东西并更新帖子。

于 2016-01-28T16:02:33.777 回答