1

我正在寫一個基於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  

有沒有一種方法可以使用更簡潔的語法呢?與我期待寫的相比,我發現目前的實現難以閱讀和推理。

+0

雖然這是有點不必要的,不是嗎?有很多方法可以模擬仍然是單行條件列表的理解。例如使用條件運算符:'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)]'。沒有必要經歷所有的麻煩...... –

+0

@TasosPapastylianou *一個班輪*與**簡潔**不一樣,即使我使用了其中一種方法,它也會與宏組合(所以我可以按照我想要的方式寫它),因爲我不想寫你所建議的(一遍又一遍),這正是宏的目的。這個宏是我學習元編程的一個練習,所以我認爲這是值得的麻煩,從你的建議我只考慮最後一個,用'過濾器',因爲它的性能相當於'[i^3 for i in 1:n if i%2 == 0]'在Julia v0.5.0-rc0中,還有我的'@ vcomp'宏。 – SalchiPapa

+0

好吧,夠公平的。我同意「oneliner!=簡潔」,但我認爲上述所有三個版本(如果分成兩行)變得非常易讀並且合理簡潔,而使用難以理解的宏對於原作者以外的任何人都不會理解。但我同意,這是一個非常好的宏,用於實驗該語言的目的:) 雖然很快就會有官方版本發佈,但我不知道爲什麼它從一開始就不被認爲是必不可少的。 –

回答

5

首先,我相信帶衛兵的理解來到茱莉亞(v0.5?)。

要回答您的問題:解析器希望能夠驗證其輸入在語法上是否正確,而無需查看插值的實際值。試試例如:

x, y = :i, :(1:10) 
quote 
    for $x = $y 
    end 
end 

現在解析器可以識別語法的相關部分。 (如果你使用for $x in $y代替,你應該得到相同的AST。)

+1

公平起見,我沒有看到在降低而不是解析期間無法產生此錯誤的原因。 '讓x + y'也是錯誤的,但是這個錯誤是一個降低錯誤。 –

+2

與衛兵的複合現在可用0.5-pre。 –

+0

感謝您的解釋,我同意@Fenggyang Wang的意見,認爲這應該是一個降低錯誤,我會向朱莉亞 - 開發人員詢問這個問題。 – SalchiPapa