如何插入 Julia "for" 表达式?
How to interpolate into a Julia "for" expression?
我正在编写一个宏 @vcomp
(向量理解),它基于 Python 的列表理解,带有条件子句以简洁地过滤元素方式。
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, ...)
来完成它,如上所示,据我所知这是一个解析器错误(这是一个错误吗?)。我也一直找不到这种插值的例子,这是我试过的:
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
有什么方法可以改用更简洁的语法吗?与我期望编写的内容相比,我发现当前的实现难以阅读和推理。
首先,我相信 Julia 正在接受守卫的理解(在 v0.5 中?)。
回答您的问题:解析器希望能够验证其输入在语法上是否正确,而无需查看内插的实际值。尝试例如
x, y = :i, :(1:10)
quote
for $x = $y
end
end
现在解析器可以识别语法的相关部分。 (如果你改用 for $x in $y
,你应该得到相同的 AST。)
我正在编写一个宏 @vcomp
(向量理解),它基于 Python 的列表理解,带有条件子句以简洁地过滤元素方式。
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, ...)
来完成它,如上所示,据我所知这是一个解析器错误(这是一个错误吗?)。我也一直找不到这种插值的例子,这是我试过的:
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
有什么方法可以改用更简洁的语法吗?与我期望编写的内容相比,我发现当前的实现难以阅读和推理。
首先,我相信 Julia 正在接受守卫的理解(在 v0.5 中?)。
回答您的问题:解析器希望能够验证其输入在语法上是否正确,而无需查看内插的实际值。尝试例如
x, y = :i, :(1:10)
quote
for $x = $y
end
end
现在解析器可以识别语法的相关部分。 (如果你改用 for $x in $y
,你应该得到相同的 AST。)