2017-04-23 27 views
10

從python3到Julia,我們希望能夠使用yield/yield語法或類似的東西寫出快速迭代器。是否有一個宏,用於在茱莉亞從類似生成器的函數創建快速迭代器?

朱莉婭的宏似乎表明,可以建立一個宏,將這種「生成器」函數轉換爲朱利亞迭代器。 [它甚至好像你可以寫在功能的風格,這是一個特點Iterators.jl包也試圖提供其具體迭代器https://github.com/JuliaCollections/Iterators.jl#the-itr-macro-for-automatic-inlining-in-for-loops容易在線迭代器]

只給什麼,我有一個例子記:

@asiterator function myiterator(as::Array) 
    b = 1 
    for (a1, a2) in zip(as, as[2:end]) 
    try 
     @produce a1[1] + a2[2] + b 
    catch exc 
    end 
    end 
end 

for i in myiterator([(1,2), (3,1), 3, 4, (1,1)]) 
    @show i 
end 

其中myiterator應該理想地創建具有低開銷儘可能快速迭代。當然這只是一個具體的例子。理想情況下,我想有一些適用於所有或幾乎所有發生器功能的東西。

目前推薦的將生成器函數轉換爲迭代器的方法是通過Julia的任務,至少據我所知。但是他們似乎也比單純的迭代器慢得多。例如,如果你可以使用簡單的迭代器(如imap,chain等)(由Iterators.jl包提供)來表示你的函數,這似乎是非常可取的。

理論上可以在茱莉亞建立一個宏轉換生成器式的功能變成靈活的快速迭代器?

Extra-Point-Question:如果這是可能的,是否會有一個通用宏來嵌入這樣的迭代器?

+0

「Channel」代替0.6中的Task,應該比「Task」快得多。 –

+0

我還沒有聽說過他們,我讀了很多關於朱莉婭的文件。感謝指針! – schlichtanders

+0

渠道不會取代任務---它們是任務可用於溝通的機制。 –

回答

2

Python風格的生成器 - 在Julia最接近從任務中產生 - 涉及相當多的固有開銷。你必須切換任務,這是不平凡的,不能直接被編譯器消除。這就是爲什麼Julia的迭代器是基於函數來轉換一個通常不可變的,簡單的狀態值和另一個。長話短說:不,我不相信這種轉變可以自動完成。

2

這種形式的一些迭代可以寫成如下:

myiterator(as) = (a1[1] + a2[2] + 1 for (a1, a2) in zip(as, as[2:end])) 

此代碼可以(可能)被內聯。

爲了完全概括這一點,理論上可以編寫一個將其參數轉換爲continuation-passing style(CPS)的宏,從而可以掛起並重新啓動執行,並提供類似於迭代器的內容。分隔延續尤其適用於此(https://en.wikipedia.org/wiki/Delimited_continuation)。其結果是一大堆匿名函數,它可能比任務切換快,但不一定,因爲在一天結束時它需要堆 - 分配類似數量的狀態。

我正好這裏有這樣一個轉變的一個例子(femtolisp雖然不是朱莉婭):https://github.com/JeffBezanson/femtolisp/blob/master/examples/cps.lsp 這與define-generator宏,做你的描述結束。但我不確定爲Julia做這件事是值得的。

1

在思考了很多如何將python生成器轉換爲Julia而沒有失去很多性能之後,我實現並測試了一個高級函數庫,它以延續風格實現類Python /類任務生成器。 https://github.com/schlichtanders/Continuables.jl

本質上,這個想法是把Python的yield/Julia的produce作爲一個函數,我們從外部作爲一個額外的參數。我叫它cont繼續。尋找例如在此重新實現了一系列

crange(n::Integer) = cont -> begin 
    for i in 1:n 
    cont(i) 
    end 
end 

您可以簡單地通過下面的代碼總結所有整數

function sum_continuable(continuable) 
    a = Ref(0) 
    continuable() do i 
    a.x += i 
    end 
    a.x 
end 

# which simplifies with the macro [email protected] to 
@Ref function sum_continuable(continuable) 
    a = Ref(0) 
    continuable() do i 
    a += i 
    end 
    a 
end 

sum_continuable(crange(4)) # 10 

正如你希望同意,你可以用continuables幾乎就像你會努力工作python中的生成器或茱莉亞的任務。使用do符號而不是for循環是您必須習慣的一件事。

這個想法帶你真的很遠。使用這種思想不能純粹實現的唯一標準方法是zip。所有其他標準的高級工具的工作方式與您希望的一樣。

性能比任務快得多,甚至比迭代器在某些情況下更快(特別是Continuables.cmap的天真實現比Iterators.imap快幾個數量級)。查看GitHub存儲庫https://github.com/schlichtanders/Continuables.jl的Readme.md以獲取更多詳細信息。


編輯:爲了更直接回答我的問題,沒有必要對宏@asiterator,只需直接使用延續風格。

mycontinuable(as::Array) = cont -> begin 
    b = 1 
    for (a1, a2) in zip(as, as[2:end]) 
    try 
     cont(a1[1] + a2[2] + b) 
    catch exc 
    end 
    end 
end 

mycontinuable([(1,2), (3,1), 3, 4, (1,1)]) do i 
    @show i 
end