问题:是否可以创建一个Elixir Stream
,它在内部保留某种状态,随着每次调用而变化?代码看起来会怎么样?
除了普通的Stream
模块之外,我很高兴看到Elixir 0.10.2有Enum
s。虽然有关于如何使用Stream
的文档,但几乎没有文档,也没有描述如何创建它们的全局可搜索文档。阅读Stream
模块的源代码并不是很有启发性。我也研究了Lazily concatenate an enumerable of lists及其答案,虽然非常有趣但它也没有触及改变状态。
考虑生成Fibonacci数字,您需要记住前两个值。或者想一想Clojure's lazy sequence for prime numbers。
我首先要了解这是否可行的问题是:
enumerable
元素仅由一些示例设置,并始终设置为现有的固定列表。处理无限序列的示例根本不会触及enumerable
(例如iterate
或repeat
)。fun
提供的函数应该返回另一个函数,而不是新的Stream.Lazy
记录,据我所知,这是Clojure的懒惰序列的最大区别,你返回一个新的序列lazy-seq
。acc
仍然有点超出我的范围。看起来fun
本身并没有修改它;而是调用fun
的一些外部函数。因此我的问题。
答案 0 :(得分:2)
Stream.unfold/2允许您在保持“累加器”(状态)的同时生成一系列值。
我刚刚使用它构建了一个懒惰的Fibonacci序列函数:https://github.com/seven1m/30-days-of-elixir/blob/master/24-stream.exs#L31-33
答案 1 :(得分:1)
我只是在溪边玩耍。我可能会忽略这一点,但为了维持状态,可以使用exactor(gen_server)吗?
https://github.com/sasa1977/exactor
定义
defmodule Sample do
use ExActor
defcall next(x), state: state, do: reply(state + x, x)
def fib(n) do
{:ok, act} = Sample.start(1)
Stream.iterate(0, fn(x) -> Sample.next(act, x) end) |> Enum.take(n)
end
end
运行
iex(3)> Sample.fib(5)
[0, 1, 1, 2, 3]
iex(4)> Sample.fib(10)
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
答案 2 :(得分:1)
我不是懒惰流的专家,但这是我的尝试:https://gist.github.com/sasa1977/6699577
但请注意,在此示例中,状态是公共的,而不是内部的,因此我不知道这是否是您正在寻找的。 p>
解决方案首先依赖于创建记录(在本例中为private),并公开一个可以将记录移动到下一个状态的函数:
fib0 = Fibonacci.new
fib1 = Fibonacci.next(fib0)
fib2 = Fibonacci.next(fib1)
fib3 = Fibonacci.next(fib2)
IO.inspect fib3
每个fibx
包含完整状态。在这种情况下,我保留当前的数字,加上之前生成的两个数字。
基于此,我现在可以使用Stream.iterate
制作无休止的懒人集合:
Fibonacci.lazy
|> Enum.take(5)
|> IO.inspect
由于它基于Fibonacci.next
,因此每个生成的元素都是记录,而不是值。您可以使用Fibonacci.value
从每个元素中提取值。
也可以执行更复杂的操作:
Fibonacci.lazy
|> Stream.drop(3) # drop first three
|> Enum.first # take the first
|> Fibonacci.lazy # start lazy from it
|> Stream.filter(&(rem(Fibonacci.value(&1), 2) == 1)) # take only odd values
|> Stream.map(&(Fibonacci.value(&1))) # extract values from it
|> Enum.take(10) # take first 10
|> IO.inspect # print it