2017-02-23 49 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。