首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >递归让步时递归块扩展错误

递归让步时递归块扩展错误
EN

Stack Overflow用户
提问于 2018-03-22 23:55:37
回答 1查看 152关注 0票数 3

我希望在Crystal中实现Python的os.walk方法。我试图以递归的方式来做这件事,但是编译器告诉我要小心递归让步,因为它在编译时会递归地/无限地生成代码。这是我所拥有的

代码语言:javascript
复制
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
EN

回答 1

Stack Overflow用户

发布于 2018-03-23 00:15:16

Gitter上一些乐于助人的社区成员给我指明了正确的方向,他们只是想在这里分享我的经验。答案是,你不能递归地使用yield,但是你必须使用block变量来代替(后面会有解释)。这是我最终得到的结论:

代码语言:javascript
复制
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

这里的诀窍是,您必须使用block.call,而不是使用yield关键字,并转发您的块。这实际上是文档中的already,但有点微妙。在编译过程中,如果你有一个yield,编译器会直接内联你的代码块(据我所知)。当使用block.call时,会创建一个函数,这就是我们需要键入块参数的原因。如果你不给它一个类型,block.call将会期望0个参数。要传递东西,只需输入它,就像我在这个方法签名中那样。

基于上面的解释,这就解释了为什么你不需要在block中添加一个类型,而你只需要让它正常工作。理解为什么yieldblock.call之间存在性能差异也很重要,因为在一种情况下,会创建一个闭合函数,而不是内联代码的编译器。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/49433138

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档