2016-01-28 2 views
2

Tenir compte de la fonction suivante:Créer des variables locales par programmation à partir d'un dictionnaire Julia

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

end 

Je veux créer des variables locales dans un périmètre de » func, où: noms de variables sont chacun la clé de la vars et les valeurs de variable sont des valeurs dans un vars correspondant à la clé donnée.

Je sais que je pourrais utiliser eval() quelque chose comme:

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

Cependant, les variables qui définira dans la portée globale, ce qui a des répercussions sur la performance et pourraient remplacer les variables globales existantes.

P.S. J'ajouterai un contexte au cas où vous auriez des suggestions radicalement différentes de l'approche que je préconise. J'implémente un wrapper autour du JuMP permettant aux utilisateurs de créer dynamiquement des modèles d'optimisation avec des expressions de contraintes arbitraires. Étant donné que les expressions de contrainte sont définies à l'aide de symboles et que JuMP requiert la définition de ces symboles dans la portée actuelle, je souhaite que l'utilisateur fournisse ces symboles et leurs valeurs dans un dictionnaire à la fonction. La définition globale des symboles serait lourde car l'utilisateur devrait idéalement pouvoir exécuter la fonction plusieurs fois avec les mêmes expressions de contrainte (c'est-à-dire les mêmes symboles) mais des valeurs différentes.

+0

vous devriez [escape] (http://docs.julialang.org/en/latest/manual/metaprogramming/#hygiene) l'expression par 'esc (: ($ k = $ v))' – Gnimuc

+0

@GnimucK. Mais cela ne résoudra toujours pas le problème de la déclaration des variables dans une portée globale. – aberdysh

+1

hmm ... en effet, ça ne marchera pas. est-il possible de construire ce wrapper dans un module? 'eval' va générer des variables dans la portée du module. – Gnimuc

Répondre

1

Note: Je ne suis pas un expert en métaprogrammation!

I pensez vous pourriez faire quelque chose comme ça, pas avec des fonctions génériques, mais avec une macro ou une fonction générée à la place.

Sur cette base:

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 

j'ai pu faire quelque chose comme ça, il pourrait en faire plus la vérification des erreurs et aussi macros génériques rendrait cela plus facile (au moins pour le contrôle d'erreur) dans la branche de développement:

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" 

Avis vous ne pouvez pas créer un varible dict puis passer à la macro @foo (encore une fois generic macros pourrait rendre cela plus facile car il pourrait y avoir une méthode pour Symbol aussi et envoyer une erreur):

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

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

A ce quelque chose comme point:

@foo a 1 b 2 c 3 

ou

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

serait mieux A MON HUMBLE AVIS.

J'apprécierais beaucoup si quelqu'un m'explique pourquoi ce n'est pas optimal! Cette réponse aurait dû être un commentaire, mais c'est beaucoup de code.

Je vais essayer d'implémenter quelque chose de similaire avec des macros génériques et des fonctions générées et mettre à jour le post.