2017-10-03 5 views
1

Je suis en train d'écrire une macro Julia qui transforme ceci:Julia macro pour transformer `f (dim1, dim2, ..) = value` dans` f (valeur, dim1, dim2, ..) `

[par1!(par2(d1,d2)+par3(d1,d2) ,d1,d2,dfix3) for d1 in DIM1, d2 in DIM2] 

(pas très inspirant) en quelque chose de beaucoup plus lisible, comme ceci:

@meq par1!(d1 in DIM1, d2 in DIM2, dfix3) = par2(d1,d2)+par3(d1,d2) 

par1!() est une fonction pour définir des données multidimensionnelles et par2() est un getData() - type de fonction. J'essaie de l'implémenter en utilisant une macro, mais comme je suis sur ma première expérience avec julia marcro, je ne sais pas comment "assembler" l'expression finale des différentes pièces. Voici ce que je fait jusqu'à présent:

macro meq(eq) 
    # dump(eq) 
    lhs_par    = eq.args[1].args[1] 
    rhs     = eq.args[2] 
    lhs_dims    = eq.args[1].args[2:end] 
    loop_counters   = [d.args[2] for d in lhs_dims if typeof(d) == Expr] 
    loop_sets    = [d.args[3] for d in lhs_dims if typeof(d) == Expr] 
    loop_wholeElements = [d for d in lhs_dims if typeof(d) == Expr] 
    lhs_dims_placeholders = [] 
    for d in lhs_dims 
     if typeof(d) == Expr 
      push!(lhs_dims_placeholders,d.args[2]) 
     else 
      push!(lhs_dims_placeholders,d) 
     end 
    end 
    outExp = quote 
     [$(lhs_par)($(rhs),$(lhs_dims_placeholders ...)) for $(loop_wholeElements ...) ] 
    end 
    #show(outExp) 
    return outExp 
end 

Cependant, la macro ci-dessus ne compile pas et renvoie une erreur de syntaxe (« spécification d'itération non valide ») en raison de la partie for $(loop_wholeElements) ... en effet, je ne sais pas comment traiter les expressions lhs_dims_placeholders et loop_wholeElements pour "assembler" l'expression développée ...

EDIT:

L'exemple affiché, avec d1, d2 et dfix3, est seulement un cas particulier, mais la macro doit être capable de gérer selon les dimensions sont bouclées pour .. Je pense que la mise en macro, il fait que , mais je ne sais pas comment construire l'expression finale .. :-(

Répondre

2

au lieu de faire manuellement les args choses correspondant codé en dur, nous pourrions utiliser MacroTools.jl comme un outil pratique pour la correspondance de modèle:

julia> using MacroTools 

julia> macro meq(ex) 
      @capture(ex, f_(d1_ in dim1_, d2_ in dim2_, dfix3_) = body__) 
      ret = :([$f($(body[]), $d1, $d2, $dfix3) for $d1 in $dim1, $d2 in $dim2]) 
     end 
@meq (macro with 1 method) 

julia> prettify(@macroexpand @meq par1!(d1 in DIM1, d2 in DIM2, dfix3) = par2(d1,d2)+par3(d1,d2)) 
:([(Main.par1!)((Main.par2)(lobster, redpanda) + (Main.par3)(lobster, redpanda), lobster, redpanda, Main.dfix3) for lobster = Main.DIM1, redpanda = Main.DIM2]) 

MISE À JOUR:

L'expression finale souhaitée est un comprehension, il semble que, pour une raison Julia ne pouvait pas comprendre for expr (où $expr #=> XXX in XXX) est une compréhension. La solution utilise directement its lowered form:

julia> using MacroTools 

julia> par1(a, b, c, d) = a + b + c + d 
par1 (generic function with 1 method) 

julia> par2(a, b) = a + b 
par2 (generic function with 1 method) 

julia> macro meq(ex) 
      @capture(ex, par_(dims__) = rhs_) 
      loopElements = [] 
      dimsPlaceholders = [] 
      for d in dims 
       @capture(d, di_ in DIMi_) || (push!(dimsPlaceholders, d); continue) 
       # push!(loopElements, x) 
       push!(loopElements, :($di = $DIMi)) 
       push!(dimsPlaceholders, di) 
      end 
      ret = Expr(:comprehension, :($par($(rhs),$(dimsPlaceholders...))), loopElements...) 
     end 
@meq (macro with 1 method) 

julia> prettify(@macroexpand @meq par1!(d1 in DIM1, d2 in DIM2, dfix3) = par2(d1,d2)+par3(d1,d2)) 
:($(Expr(:comprehension, :((Main.par1!)(begin 
      (Main.par2)(bee, wildebeest) + (Main.par3)(bee, wildebeest) 
     end, bee, wildebeest, Main.dfix3)), :(bee = Main.DIM1), :(wildebeest = Main.DIM2)))) 

julia> @meq par1(m in 1:2, n in 4:5, 3) = par2(m,n) + par2(m,n) 
2×2 Array{Int64,2}: 
18 21 
21 24 

Notez que, la portée variable de d1,d2 dans l'expression générée tort si nous utilisons push!(loopElements, x) plutôt que push!(loopElements, :($di = $DIMi)). Attendons quelqu'un de compétent pour donner une explication approfondie.

+0

Nous vous remercions de votre aide! Je vais essayer MacroTool.jl si je ne peux vraiment pas obtenir la composition de l'expression finale dans la macro que j'ai écrite – Antonello

+0

@Antonello votre macro devrait fonctionner au premier coup d'oeil, mais pour une raison que je ne peux pas expliquer , ça ne marche pas (voir mon edit ci-dessus). – Gnimuc

+0

Merci .. Je pensais la même chose, que le problème est dû à être une compréhension .. Je n'ai pas accès à un pc maintenant, demain je vais essayer .. sinon je peux toujours construire une "chaîne de code" morceau par morceau, puis utilisez parse() pour générer une expression .. – Antonello

1

Si vous ne voulez pas compter sur un package externe pour cela, la solution que je fournis sur le discours Julia devrait également travailler

return :([$(Expr(:generator,:($(Expr(:call,lhs_par,rhs,lhs_dims_placeholders...))),loop_wholeElements...))]) 

La clé est d'utiliser le: constructeur de générateur pour faire l'expression de la boucle

De plus, rhs peut être remplacé par rhs.args [n] afin d'éliminer le bloc quote et d'insérer directement l'expression.

Ce produit alors l'expression exacte:

:([(par1!(par2(d1, d2) + par3(d1, d2), d1, d2, dfix3) for d1 in DIM1, d2 in DIM2)]) 

EDIT:

Bon, alors je suis allé de l'avant et testé:

return Expr(:comprehension,Expr(:generator,Expr(:call,lhs_par,rhs.args[2],lhs_dims_placeholders...),loop_wholeElements...)) 

fin

calcul Ensuite, le résultat comme ça

meq(:(par1!(d1 = 1:2, d2 = 1:2, 3) = par2(d1,d2)+par3(d1,d2))) |> eval 
+0

Juste pour mentionner que l'utilisation directe du 'Expr'' x dans X's dans 'loop_wholeElements' provoquera un problème de portée, nous devons changer' x dans X' en 'x = X' pour corriger cela, jetez un oeil à l'exemple dans mon post mis à jour. – Gnimuc

+0

Merci. (a) Il y a un problème avec l'utilisation de l'expression du générateur, dans la compréhension étant entourée d'une parenthèse supplémentaire ('[(expr)]' au lieu de '[expr]'); (b) Je dois garder 'rhs', comme si je le remplaçais par' rhs.args [2] 'et le rhs contient d'autres compréhensions, leurs parties de boucles sont transformées de' pour a dans, b dans B' à 'pour a = A, b = B'. Est-ce un bug de Julia? – Antonello

+0

@Antonello Je ne suis pas sûr que ce soit un bug ou non. J'ai posté le MWE sur [discours] (https://discourse.julialang.org/t/help-in-making-multi-dimensional-equations-a-bit-sexier/6217/10). – Gnimuc