2017-02-23 52 views
2

我喜歡長生不老藥。特別是我有一個函數,它構造了一個無限的素數流;您可以使用適當的Stream/Enum操作(分別爲Enum.take(10_000)Enum.take_while(& (&1 < 1_000_000)))獲取前10,000或全部低於1,000,000的素數或其他值。如何獲取流的元素(Elixir)並保持流的狀態?

但假設我不知道我需要多少素數。我明白了,我說,嘿,實際上需要另外一千個素數。有沒有辦法說,獲取流的前10,000個元素,然後以某種方式保存生成的流對象,以便按需(當然重複)獲得下一個 1,000?

+2

這看起來像你想要的:https://github.com/tallakt/stream_split/blob/master/lib/stream_split.ex。我試着先用'Enumerable.reduce'實現類似的自己,但後來決定搜索現有的解決方案,因爲它不是非常簡單。 – Dogbert

回答

1

TL; DR保存累加器,而不是「流」。

的強大的解決方案是通過@Dogbert在給予:StreamSplit包似乎permorm正是有人問。爲了歷史的緣故,我的回答是:有許多Stream函數(全部源自Stream.transform/4,這是一個幾乎可能需要的所有東西的通用流實現),可能會實現這個功能。例如,考慮斐波納契數字。

stream = Stream.iterate({1, 1}, fn {acc, i} -> 
    {acc + i, acc} 
end) 
#⇒ #Function<61.36862645/2 in Stream.unfold/2> 
stream |> Enum.take(5) 
#⇒ [{1, 1}, {2, 1}, {3, 2}, {5, 3}, {8, 5}] 
current = stream |> Enum.take(5) |> List.last 
#⇒ {8, 5} 

如果你想繼續獲取數字:

#    ⇓⇓⇓⇓⇓⇓ 
Stream.iterate({8, 5}, fn {acc, i} -> 
    {acc + i, acc} 
end) 

只要保持在一箇中間狀態,並把它作爲一個初始值碼流功能使用的是得到的素數作爲一個可能實現它們。我個人認爲在保持「尾巴」而不是累加器方面有任何優勢,但我可能肯定是錯的。

+0

這當然有效,但要麼(a)重複生成代碼(在您的示例中很容易,但在其他示例中不容易)或(b)離開流生態系統,這很奇怪。我很驚訝Stream中沒有實現這個功能...... –

+0

我相信你可以保存流狀態,但它看起來有點複雜:https://github.com/tallakt/stream_split/blob/master /lib/stream_split.ex。 – Dogbert

+0

@Dogbert這是一個人爲的例子,當與Stream.zip一起使用時可能會失敗。任何'Stream' reducer在迭代步驟中都接受'{:suspend,acc}'元組,所以實現這個不應該那麼困難。不幸的是,'Stream' API還不夠成熟,其中一半使用惰性['%Stream {}'](https://github.com/elixir-lang/elixir/blob/master/lib/elixir/lib/ stream.ex#L1368)reducer,另一半使用簡單的'function/2' reducer。此外,它部分混合了內部和外部狀態。我會保存蓄能器,這是更健壯和自然。 – mudasobwa

0

您對Streams有一個基本的誤解。流是關於創建函數的組合,以便您可以對枚舉進行復雜處理,只需通過原始枚舉一次即可完成。

很容易將Stream與服務混淆起來,並且有足夠的挖掘機可以「暫停」Stream來創建類似的服務。但是,真正想要的是Prime服務器。當你開始思考「狀態」時,你應該考慮一個GenServer。