我正在寫一個基於Python的列表解析和一個條件子句以簡潔的方式過濾元素的宏@vcomp
(矢量解釋)。如何插入Julia「for」表達式?
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
有沒有一種方法可以使用更簡潔的語法呢?與我期待寫的相比,我發現目前的實現難以閱讀和推理。
雖然這是有點不必要的,不是嗎?有很多方法可以模擬仍然是單行條件列表的理解。例如使用條件運算符:'deleteat!((A = 1,10; A [A%2。== 0])],使用邏輯索引和一個複合語句:'[i^3' [i%2 == 0?i^3:在1:10中我什麼都沒有]; A),find(A。== nothing))',或者使用過濾器函數:'[i^3 for i in filter ((x) - > x%2 == 0,1:10)]'。沒有必要經歷所有的麻煩...... –
@TasosPapastylianou *一個班輪*與**簡潔**不一樣,即使我使用了其中一種方法,它也會與宏組合(所以我可以按照我想要的方式寫它),因爲我不想寫你所建議的(一遍又一遍),這正是宏的目的。這個宏是我學習元編程的一個練習,所以我認爲這是值得的麻煩,從你的建議我只考慮最後一個,用'過濾器',因爲它的性能相當於'[i^3 for i in 1:n if i%2 == 0]'在Julia v0.5.0-rc0中,還有我的'@ vcomp'宏。 – SalchiPapa
好吧,夠公平的。我同意「oneliner!=簡潔」,但我認爲上述所有三個版本(如果分成兩行)變得非常易讀並且合理簡潔,而使用難以理解的宏對於原作者以外的任何人都不會理解。但我同意,這是一個非常好的宏,用於實驗該語言的目的:) 雖然很快就會有官方版本發佈,但我不知道爲什麼它從一開始就不被認爲是必不可少的。 –