Elixir:Stream.drop_while 泛化?

Elixir: Stream.drop_while generalisation?

我想概括这段代码,但看不出如何巧妙地做到这一点:

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 ) 

如果可以的话,我希望不要探索宏。

流是一种数据结构,这意味着您可以对其进行缩减,在每一步将其细化为一组特定的值:

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 (专用于副作用)会更好。