我如何看待Elixir中的这个序列生成器?

kon*_*gun 1 elixir

我需要生成一个这样的序列:
[1, 3, 5, 7, 9, 13, 17, 21, 25, 31, 37, 43, 49, 57, 65, 73, 81]

(本例中为17个数字)

算法是:
[1, (previous + 2), (previous + 2), (previous + 2), (previous + 2), (previous + 4), (previous + 4), (previous + 4), (previous + 4) ...

所以4个第一项是+2,接下来4个是+4,接下来是4 +.每4个项增加2.

我能够在Ruby中做一个快速和hacky版本:

def sequence
  incr = 0
  (0..16).each.inject([]) do |acc, counter|
    acc << (acc.last || 1) + incr
    incr += 2 if counter.modulo(4) == 0
    acc
  end
end
Run Code Online (Sandbox Code Playgroud)

但是我在Elixir做同样的问题 - 结果却是超级跛脚.像这样:

def sequence do
  { sequence, _ } =
    0..16
    |> Enum.reduce({[], 0}, fn(counter, {result, incr}) ->
      last = List.last(result)
      if last do
        result = result ++ [last + incr]
      else
        result = [1]
      end
      if rem(counter, 4) == 0 do
        incr = incr + 2
      end
      {result, incr}
    end)
  sequence
end
Run Code Online (Sandbox Code Playgroud)

显然我不应该在这里强制性地思考,但对于这个问题我不能:D我也确定有一种方法,管道更原子.

如何以Elixir方式解决这个问题?

Dog*_*ert 5

我将启动累加器{[1], 0}以删除函数体中的特殊情况.List.last并且++通常不推荐,因为它们效率低(O(n)).Elixir中的惯用方法是反向构建列表并在结尾处反转列表.这意味着您的List.last逻辑现在可以通过匹配列表头部的模式来处理,这很便宜.您还应该收到incrif内部分配的警告.惯用的方法是做类似的事情incr = if ..., do: incr + 2, else: incr.

这是我写这个的方式:

(0..16)
|> Enum.reduce({[1], 0}, fn counter, {[h | _] = result, incr} ->
  incr = if rem(counter, 4) == 0, do: incr + 2, else: incr
  {[h + incr | result], incr}
end)
|> elem(0)
|> Enum.reverse
|> IO.inspect
Run Code Online (Sandbox Code Playgroud)

输出:

[1, 3, 5, 7, 9, 13, 17, 21, 25, 31, 37, 43, 49, 57, 65, 73, 81, 91]
Run Code Online (Sandbox Code Playgroud)