Ich schrieb ein Makro @vcomp
(Vektor Verständnis) basierend auf Python Listen-Comprehensions mit einer bedingten Klausel, um Elemente in einer prägnanten Art und Weise zu filtern.Wie interpoliert man in einen Julia "for" Ausdruck?
macro vcomp(comprehension::Expr, when::Symbol, condition)
comp_head, comp_args = comprehension.head, comprehension.args
comp_head ∉ [:comprehension, :typed_comprehension] && error("@vcomp not a comprehension")
when ≠ :when && error("@vcomp expected `when`, got: `$when`")
T = comp_head == :typed_comprehension ? comp_args[1] : nothing
if VERSION < v"0.5-"
element = comp_head == :comprehension ? comp_args[1] : comp_args[2]
sequence = comp_head == :comprehension ? comp_args[2] : comp_args[3]
else
element = comp_head == :comprehension ? comp_args[1].args[1] : comp_args[2].args[1]
sequence = comp_head == :comprehension ? comp_args[1].args[2] : comp_args[2].args[2]
end
result = T ≠ nothing ? :($T[]) : :([])
block = Expr(:let, Expr(:block,
Expr(:(=), :res, result),
Expr(:for, sequence,
Expr(:if, condition,
Expr(:call, :push!, :res, element))),
:res))
return esc(block)
end
wie folgt verwendet:
julia> @vcomp Int[i^3 for i in 1:10] when i % 2 == 0
5-element Array{Int64,1}:
8
64
216
512
1000
auf das sich diese erweitern:
julia> macroexpand(:(@vcomp Int[i^3 for i in 1:15] when i % 2 == 0))
:(let
res = Int[]
for i = 1:15
if i % 2 == 0
push!(res,i^3)
end
end
res
end)
Ich hatte erwartet, der Lage sein, dies zu schreiben block
wie:
block = quote
let
res = $result
for $sequence
if $condition
push!(res, $element)
end
end
res
end
end
Welche der folgenden Fehler gibt:
ERROR: syntax: invalid iteration specification
Statt der Art und Weise kam ich mit:
block = Expr(:let, Expr(:block,
Expr(:(=), :res, result),
Expr(:for, sequence,
Expr(:if, condition,
Expr(:call, :push!, :res, element))),
:res))
aber ich war in der Lage, es zu tun Expr(:for, ...)
direkt, wie oben gezeigt, und so weit ich das verstehe, ist das ein Parserfehler (ist das ein Bug?). Ich habe auch nicht in der Lage gewesen, Beispiele für diese Art von Interpolation zu finden, das ist, was ich versucht habe:
julia> ex₁ = :(i in 1:10)
:($(Expr(:in, :i, :(1:10))))
julia> ex₂ = :(i = 1:10)
:(i = 1:10)
julia> quote
for $ex₁
ERROR: syntax: invalid iteration specification
julia> quote
for $ex₂
ERROR: syntax: invalid iteration specification
Construct ganzen Ausdruck und prüfen:
julia> ex₃ = quote
for i in 1:10
print(i)
end
end
quote # none, line 2:
for i = 1:10 # none, line 3:
print(i)
end
end
julia> ex₃.args
2-element Array{Any,1}:
:(# none, line 2:)
:(for i = 1:10 # none, line 3:
print(i)
end)
julia> ex₃.args[2].args
2-element Array{Any,1}:
:(i = 1:10)
quote # none, line 3:
print(i)
end
julia> ex₃.args[2].args[1]
:(i = 1:10)
julia> ex₃.args[2].args[1] == ex₂ # what's the difference then?
true
Das funktioniert aber weniger lesbar:
julia> ex₄ = Expr(:for, ex₁, :(print(i)))
:(for $(Expr(:in, :i, :(1:10)))
print(i)
end)
julia> ex₅ = Expr(:for, ex₂, :(print(i)))
:(for i = 1:10
print(i)
end)
julia> eval(ex₃)
12345678910
julia> eval(ex₄)
12345678910
julia> eval(ex₅)
12345678910
Gibt es eine Möglichkeit, die eher knappe Syntax stattdessen verwenden? Ich finde die aktuelle Implementierung schwierig zu lesen und zu begründen im Vergleich zu dem, was ich zu schreiben erwartet hatte.
das ist alles ein bisschen unnötig, nicht wahr? Es gibt viele Möglichkeiten, das Verständnis der bedingten Listen zu emulieren, die immer noch Einzeiler sind. z.B. mit logischer Indizierung und einer zusammengesetzten Anweisung: '[i^3 für i = (A = 1:10; A [A% 2. == 0])] ', mit dem bedingten Operator:' deleteat! ((A = [i% 2 == 0? i^3: nichts für i in 1:10]; A), finde (A. == nothing)) 'oder benutze die Filterfunktion:' [i^3 für i im Filter ((x) -> x% 2 == 0, 1:10)]. Keine Notwendigkeit, durch all diese Probleme zu gehen ... –
@TasosPapastylianou * ein Liner * ist nicht das gleiche wie ** prägnant **, auch wenn ich eine Ihrer Methoden verwendet, wäre es in Kombination mit einem Makro (so kann ich schreibe es so, wie ich es will), da ich nicht das schreiben möchte, was du vorgeschlagen hast (und immer wieder) und genau das ist der Zweck von Makros.Dieses Makro war eine Übung für mich, um Metaprogrammierung zu lernen, also denke ich, dass es die Mühe wert war, von deinen Vorschlägen würde ich nur das letzte betrachten, das mit 'Filter', da seine Leistung mit' [^^ für ich in 1: n wenn ich% 2 == 0] 'in Julia v0.5.0-rc0 und auch zu meinem' @ vcomp' Makro. – SalchiPapa
Nun, fair genug. Ich stimme zu, dass "oneliner! = Prägnant", aber ich denke, dass alle drei obigen Versionen, wenn sie über zwei Zeilen verteilt sind, sehr lesbar und relativ prägnant werden, während die Verwendung eines obskuren Makros für jeden anderen als den ursprünglichen Autor weniger verständlich wäre. Aber ich stimme zu, es ist ein sehr nettes Makro, mit dem man experimentieren kann, um mit der Sprache zu experimentieren :) Es ist gut, dass bald eine offizielle Version kommt, ich weiß nicht, warum es von Anfang an nicht als wesentlich angesehen wurde . –