Recursion 递归生成时的递归块扩展错误

Recursion 递归生成时的递归块扩展错误,recursion,crystal-lang,Recursion,Crystal Lang,我希望在Crystal中实现Python的os.walk方法。我试图以递归方式进行编译,但编译器被告知要小心递归生成,因为它在编译时会以递归/无限方式生成代码。这就是我所拥有的 def walk(d = @root, &block) d = Dir.new(d) if d.is_a?(String) dirs, files = d.entries.partition { |s| Dir.exists?(File.join(d.path, s)) } if Dir.exists

我希望在Crystal中实现Python的os.walk方法。我试图以递归方式进行编译,但编译器被告知要小心递归生成,因为它在编译时会以递归/无限方式生成代码。这就是我所拥有的

def walk(d = @root, &block)
  d = Dir.new(d) if d.is_a?(String)
  dirs, files = d.entries.partition { |s| Dir.exists?(File.join(d.path, s)) }
  if Dir.exists?(d.path)
    yield d.path, dirs, files
    dirs.each do |dir_name|
      # recursively yield
      walk File.join(d.path, dir_name), do |a, b, c|
        yield a, b, c
      end
    end
  end
end

Gitter社区中一些有帮助的成员为我指明了正确的方向,他们只是想在这里分享我的经验。答案是,您不能递归地使用
yield
,但必须使用
block
变量(后面的解释)。以下是我最终得到的结果:

def walk(d = @root, &block : String, Array(String), Array(String) -> )
  d = Dir.new(d) if d.is_a?(String)
  dirs, files = d.children.partition { |s| Dir.exists?(File.join(d.path, s)) }
  block.call(d.path, dirs, files)
  dirs.each do |dir_name|
    walk File.join(d.path, dir_name), &block
  end
end
这里的诀窍是,不要使用
yield
关键字,而必须使用
块。调用
并转发块。这实际上在文档中,但有点微妙。在编译过程中,如果您有一个
成品率
,编译器将直接在成品率所在的块中内联(据我所知)。使用
block.call
时,会创建一个函数,这就是我们需要键入block参数的原因。如果不为其指定类型,
block.call
将需要0个参数。要传递信息,只需键入类似于我在这个方法签名中所做的操作


基于上面的解释,当您只是屈服并且它可以正常工作时,为什么不需要向
添加类型是有道理的。理解为什么
产量
块之间存在性能差异也很重要,因为在一种情况下,与内联代码的编译器相比,创建了一个关闭函数。

使用
Dir\u子项而不是
Dir\35;条目
,您不需要过滤掉
。另外,在您已经为路径中的条目创建了迭代器之后,调用
Dir.exists?
也是不合逻辑的。再看一眼,
Dir#children
Dir#entries
的替代品-谢谢!至于
Dir#exists
,我没有检查是否存在,而是检查路径是否为目录。如果有更好的方法,请让我知道,但是查看
Dir\exists
的代码,除了在File.stat周围进行包装并检查它是否是一个目录之外,它看起来并没有做什么。不知道如何以更便宜的方式检查路径是否为目录您不需要检查
d.path
是否存在。如果不是的话,
Dir.new
已经出现了。哦,我现在明白你的意思了(看错行了)。说得好,谢谢。刚刚更新