【问题标题】:Elixir: recursive generatorsElixir:递归生成器
【发布时间】:2019-08-25 19:10:47
【问题描述】:

是否可以使用 Elixir 构建 Python 风格的递归生成器?像这样的:

def traverse(parent_dir):
    dirs, files = get_dirs_and_files_as_lists(parent_dir)

    for d in dirs:
        yield from traverse(d)

    for f in files:
        yield f

对于所有要线性处理的文件,没有不定长度的急切列表所暗示的开销:

for f in traverse(dir):
    process(f)

这或一些等效的工作应该可以使用流;不幸的是,我不知道怎么做。

我想要这样的东西,只是懒惰:

def traverse_eagerly(parent_dir) do
  {dirs, files} = get_dirs_and_files_as_lists(parent_dir)

  for x <- dirs do
    traverse_eagerly(x)
  end
  |> Enum.concat()
  |> Enum.concat(files)
end

【问题讨论】:

  • 你的问题让我想回答“我如何遍历目录”,但这不是你问的:P 也许我会自己问......

标签: recursion stream elixir generator


【解决方案1】:

这里不需要Stream,但如果你需要,这里就是:

defmodule Traverse do
  @spec traverse(root :: binary(), yielder :: (binary() -> any())) ::
     :ok | {:error, posix()}
  def traverse(root, yielder) do
    # https://hexdocs.pm/elixir/master/File.html?#ls/1
    with {:ok, list} <- File.ls(root) do
      list
      |> Stream.each(fn file_or_dir ->
        if File.dir?(file_or_dir),
          do: traverse(file_or_dir, yielder), # TCO
          else: yielder.(file_or_dir)
      end)
      |> Stream.run()
    end
  end
end

然后这样称呼它:

Traverse.traverse(".", &IO.inspect/1)

【讨论】:

  • Nb:TCO = 尾调用优化
  • TCO 与这个例子没有太大关系。是的,可以通过将处理程序作为参数传递的树 Pascal 方式。递归不会比树深度更深,顺便说一句。我在问一个树节点生成器,它在功能上(如果不是在语法上)等同于 Python 解决方案。这是一个插图,而不是对目录树遍历器的请求。
  • 我不明白问题所在。将累加器传递给yielder 函数并收集您想要的任何内容。您不应该尝试将 elixir 语法应用于 python 解决方案,Erlang 和因此 elixir 总是有更好的做事方式。
  • 当然。这一切都是为了以一种美好而惯用的方式实现我的愿望。
【解决方案2】:

解决方案似乎很简单:将Enum 替换为Stream

def traverse_lazily(parent_dir) do
  {dirs, files} = get_dirs_and_files_as_lists(parent_dir)

  for x <- dirs do
    traverse_lazily(x)
  end
  |> Stream.concat()
  |> Stream.concat(files)
end

以下按预期工作:

s = traverse_lazily(a_dir_of_choice)

for x <- s, do: whatever_you_please(x)

语言非常好。随心所欲的解决方案。除非我错过了什么,那就是:)。欢迎评论!

【讨论】:

  • Stream.flat_map(&amp;traverse_lazily/1)
  • 哦,不错。谢谢Kabie
猜你喜欢
  • 1970-01-01
  • 2021-05-22
  • 2020-08-04
  • 2016-05-03
  • 2020-05-25
  • 1970-01-01
  • 1970-01-01
  • 2020-08-16
  • 2020-02-09
相关资源
最近更新 更多