2017-01-13 6 views
2

У меня есть строка, например. z[2] и я хочу eval его в контексте, например. Dict(:z => 1:10)Лучший способ оценить в заданной области/контексте, который находится в форме Дикта?

Каков наилучший способ сделать это?

Я могу сделать вид работы, но он очень медленный.

function replace_expr(expr, d::Dict) 
    return expr 
end 

function replace_expr(s::Symbol, d::Dict) 
    get(d, s, s) 
end 

function replace_expr(expr::Expr, d::Dict) 
    return Expr(replace_expr(expr.head, d), 
       [replace_expr(e, d) for e in expr.args]...) 
end 


function eval_with(context::Dict{Symbol, Any}, expr_string::AbstractString) 
    # E.g. :abc => :(s[:abc]) 
    d = Dict(k => :(s[$(Meta.quot(k))]) for k in keys(context)) 
    ex = parse("s -> $expr_string") 
    ex = replace_expr(ex, d) 
    return eval(ex)(context) 
end 

Ниже тест

function make_context() 
    x = 1 
    y = "foo" 
    z = 2:5 

    Dict(
     :x => x, 
     :y => y, 
     :z => z 
    ) 
end 

const context = make_context() 

@test eval_with(context, "x + 3") == 4 
@test eval_with(context, "string(1, y, 1)") == "1foo1" 
@test eval_with(context, "z[2]") == 3 

@time eval_with(context, "z[2]") 
# 0.004739 seconds (767 allocations: 40.728 KB) 

ответ

2

Это похоже на место, где вы можете опереться на более Джулии встроенной оценки экспрессии машин. eval принимает необязательный аргумент: модуль, в котором должна выполняться оценка.

Вы можете создавать новые модули программно:

julia> M = Module() 
anonymous 

И вы можете присвоить значения из словаря в этот модуль с eval:

julia> context = Dict(
       :x => 1, 
       :y => "foo", 
       :z => 2:5 
      ); 

julia> for (k,v) in context 
      eval(M, :($k = $v)) 
     end 

julia> M.x 
1 

julia> M.y 
"foo" 

И теперь, конечно, вы можете оценить свой пользовательский строка в контексте вашего модуля.

julia> eval(M, parse("x+3")) 
4 

julia> eval(M, parse("string(1, y, 1)")) 
"1foo1" 

Динамичная оценка, подобная этому, не будет местом, где сияет Юлия. Я думаю, что это будет примерно так же хорошо, как он получает:

julia> @time eval(M, parse("z[2]")) 
    0.000284 seconds (13 allocations: 672 bytes) 
3 

Обратите внимание, что это имеет несколько другую семантику из кода вы написали выше; переменные в вашем контексте только заселялись в начале ... и могут быть изменены новой оценкой.

И usualcaveats об использовании eval применяются. Существуют и другие, более эффективные способы структурирования вашей программы, которые будут более эффективными, понятными и более удобными для обслуживания.

+0

Это уже огромное улучшение. Я попробовал 'eval (M,: (const $ k = $ v))' также, к сожалению, это еще не помогает. – colinfang

2

Если вы знаете значения заранее, вы можете обойтись с помощью eval с помощью метапрограммирования. Макрос для этого предоставляется Parameters.jl:

d = Dict{Symbol,Any}(:a=>5.0,:b=>2,:c=>"Hi!") 
@unpack a, c = d 
a == 5.0 #true 
c == "Hi!" #true