我正在编写一个基于Python的列表@vcomp的宏@vcomp ( 矢量理解 )和一个条件子句,以简洁的方式过滤元素。
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像这样使用:
julia> @vcomp Int[i^3 for i in 1:10] when i % 2 == 0 5-element Array{Int64,1}: 8 64 216 512 1000其中扩展到:
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)我期待能够这样写block :
block = quote let res = $result for $sequence if $condition push!(res, $element) end end res end end这给出了以下错误:
ERROR: syntax: invalid iteration specification而不是我想出的方式:
block = Expr(:let, Expr(:block, Expr(:(=), :res, result), Expr(:for, sequence, Expr(:if, condition, Expr(:call, :push!, :res, element))), :res))但是我能够直接使用Expr(:for, ...)来实现它Expr(:for, ...)如上所示,据我所知,这是一个解析器错误(这是一个错误吗?)。 我也一直无法找到这种插值的例子,这是我试过的:
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构建整个表达并检查它:
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这可行,但不易读:
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有没有办法可以使用更简洁的语法? 与我期待写的相比,我发现目前的实现难以阅读和推理。
I was writing a macro @vcomp (vector comprehension) based on Python's list comprehensions with a conditional clause to filter elements in a succinct way.
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) endUsed like this:
julia> @vcomp Int[i^3 for i in 1:10] when i % 2 == 0 5-element Array{Int64,1}: 8 64 216 512 1000Which expand to this:
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)I was expecting to be able to write block like this:
block = quote let res = $result for $sequence if $condition push!(res, $element) end end res end endWhich gives the following error:
ERROR: syntax: invalid iteration specificationInstead of the way I came up with:
block = Expr(:let, Expr(:block, Expr(:(=), :res, result), Expr(:for, sequence, Expr(:if, condition, Expr(:call, :push!, :res, element))), :res))However I was able to do it using Expr(:for, ...) directly as shown above and as far as I understand this is a parser error (is this a bug?). I have also been unable to find examples of this kind of interpolation, this is what I've tried:
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 specificationConstruct whole expression and inspect it:
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? trueThis works but is less readable:
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₅) 12345678910Is there a way I can use the more terse syntax instead? I find the current implementation difficult to read and reason about compared to what I was expecting to write.
最满意答案
首先,我相信与警卫的理解来到茱莉亚(在v0.5?)。
回答您的问题:解析器希望能够验证其输入在语法上是否正确,而无需查看插值的实际值。 尝试例如
x, y = :i, :(1:10) quote for $x = $y end end现在解析器可以识别语法的相关部分。 (如果你for $x in $y使用for $x in $y你应该得到相同的AST。)
First of all, I belive that comprehensions with guards are coming to Julia (in v0.5?).
To answer your question: The parser wants to be able to verify that its input is syntactically correct without looking into the actual value that is interpolated. Try eg
x, y = :i, :(1:10) quote for $x = $y end endNow the parser can recognize the relevant parts of the syntax. (And you should get the same AST if you use for $x in $y instead.)
更多推荐
发布评论