Elixir:Stream.drop_while泛化?

时间:2015-03-06 09:15:29

标签: stream elixir

我想概括一下这段代码,但无法以一种简洁的方式看到如何做到这一点:

defmodule Demo do

 defp side_effect(bool, label) do
   if bool do
     IO.puts label
   end
 end

 def go do
  {a,b,c} = {4,8,13}

  [2,3,4,5,7,8,9,10,11,12] # always in ascending order
  |> Stream.drop_while( fn i -> bool = (i<a); side_effect(bool, "a_#{i}"); bool end ) 
  |> Stream.drop_while( fn i -> bool = (i<b); side_effect(bool, "b_#{i}"); bool end ) 
  |> Stream.drop_while( fn i -> bool = (i<c); side_effect(bool, "c_#{i}"); bool end ) 
  |> Enum.to_list
 end
end

当它运行时(Demo.go)我得到:

a_2
a_3
b_4
b_5
b_7
c_8
c_9
c_10
c_11
c_12
[]

正如我所希望的那样 - 为输入列表中的每个元素执行副作用,并将空列表作为最终输出。 但是有可能对此进行概括,以便我可以以编程方式包含(基于列表)尽可能多的行,如下所示:

|> Stream.drop_while( fn i -> bool = (i<x); side_effect(bool, "x_#{i}"); bool end ) 

如果我能提供帮助,我希望不要探索宏。

1 个答案:

答案 0 :(得分:2)

流是一种数据结构,这意味着您可以减少它,在每一步中将其精炼为一组特定的值:

defmodule Demo do
  defp side_effect(var, threshold, label) do
    if var < threshold do
      IO.puts "#{label}_#{var}"
      true
    else
      false
    end
  end

  def go do
    values = [a: 4, b: 8, c: 13]

    stream =
      Enum.reduce(values, [2,3,4,5,7,8,9,10,11,12], fn {k, v}, acc ->
        Stream.drop_while(acc, fn i -> side_effect(i, v, k) end)
      end)

    Enum.to_list(stream)
  end
end

您还可以探索其他解决方案。例如,您可以简单地执行筛选操作,以检查值是否小于列表中的阈值,而不是为值的每个部分创建流。像这样:

defmodule Demo do
  defp side_effect(i, values) do
    pair = Enum.find(values, fn {_, v} -> i < v end)

    case pair do
      {k, v} ->
        IO.puts "#{k}_#{i}"
        false
      nil ->
        true
    end
  end

  def go do
    values = [a: 4, b: 8, c: 13]

    [2,3,4,5,7,8,9,10,11,12]
    |> Stream.filter(fn i -> side_effect(i, values) end)
    |> Enum.to_list()
  end
end

我不确定你是否真的需要过滤。如果没有,Stream.map/2或Stream.each / 2(副作用专用)会更好。