创建具有改变状态的延迟序列(流),例如斐波纳契数?

时间:2013-09-09 16:18:51

标签: elixir

问题:是否可以创建一个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

我首先要了解这是否可行的问题是:

  1. 记录的enumerable元素仅由一些示例设置,并始终设置为现有的固定列表。处理无限序列的示例根本不会触及enumerable(例如iteraterepeat)。
  2. fun提供的函数应该返回另一个函数,而不是新的Stream.Lazy记录,据我所知,这是Clojure的懒惰序列的最大区别,你返回一个新的序列lazy-seq
  3. 如何使用累加器acc仍然有点超出我的范围。看起来fun本身并没有修改它;而是调用fun的一些外部函数。
  4. 因此我的问题。

3 个答案:

答案 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

但请注意,在此示例中,状态是公共的,而不是内部的,因此我不知道这是否是您正在寻找的。

解决方案首先依赖于创建记录(在本例中为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