【问题标题】:How can I detect recursive arrays and hashes?如何检测递归数组和哈希?
【发布时间】:2014-04-11 02:16:29
【问题描述】:

如何检测包含递归结构的数组或哈希,例如下面的abc

  • 递归数组的最简单实例

    a = []
    a[0] = a
    a # => [[...]]
    
  • 递归周期/深度不是一

    b = [[], :foo]
    b[0][0] = b
    b # => [[[...]], :foo]
    
  • 非根级别的递归

    c = [a, :foo]
    c # => [[...], :foo]
    

【问题讨论】:

    标签: ruby arrays recursion hash


    【解决方案1】:

    我喜欢递归。

    这是一个不错的方法,遍历所有内容并保留您看到的对象的哈希(用于快速查找)

    class Object
      def is_recursive?(known = {})
        false
      end
    end
    
    module Enumerable
      def is_recursive?(known = {})
        return true if known.include?(self)
        known[self] = true
        begin
          any? do |*args|
            args.any?{|item| item.is_recursive?(known)}
          end
        ensure
          known[self] = false
        end
      end
    end
    
    x = []; x << x
    p x.is_recursive? # => true
    p ({x => 42}).is_recursive? # => true
    p [{foo: x}].is_recursive? # => true
    p [[[[[[:foo], {bar: [42]}]]]]].is_recursive? # => false
    

    请注意,这有点粗糙,您可能会遇到麻烦。例如,[1..Float::INFINITY].is_recursive? 会出现无限循环,尽管这很容易解决

    class Range
      def is_recursive?(known = {})
        false # optimization
      end
    end
    

    【讨论】:

    • 感谢您涵盖所有案例。
    • 加拿大人真聪明!
    【解决方案2】:

    您无法展平递归数组,因此您可以通过以下方式对其进行检查:

    begin
      a.flatten
    rescue ArgumentError => e
      e.to_s =~ /recursive/
    end
    

    【讨论】:

    • 太棒了。该错误看起来像 ArgumentError。那么,递归数组是 Array#flatten 引发 ArgumentError 的唯一原因吗?
    • @sawa 我不确定,所以最好只捕获 ArgumentError。
    • @sawa:引发错误here。引发 ArgumentError 的唯一其他方法是,如果任何元素定义了 to_ary 以某种方式引发它。
    • @xdazz 您的回答很棒,但 Marc-André Lafortune 给出了更笼统的回答,所以我正在移动验收。感谢您的帮助。
    【解决方案3】:

    您正在寻找方法include?

    a = []
    a[0] = a
    a.include? a
     => true 
    

    但这不适用于嵌套数组,作为您的第二个示例。您可以递归地执行此操作:

    def check_recursive(array, target = nil)
      target ||= array
      return true if array.include?(target)
      array.any? do |obj|
        if obj.kind_of? Array
          check_recursive(obj, target)
          obj.include?(target)
        end
      end
    end
    

    基本上有两个地方可以找到它递归:深入查看或deep-first searchbreadth-first search。最佳解决方案取决于您的问题。我的示例实现了深层搜索,这通常是个好主意。

    【讨论】:

    • 这很优雅,但是当递归不在根级别时,它会引发 stackoverflow 错误。 check_recursive([a, :bar]) # =&gt; stackoverflow.
    猜你喜欢
    • 2016-09-23
    • 1970-01-01
    • 1970-01-01
    • 2016-09-21
    • 2017-09-27
    • 1970-01-01
    • 2022-08-21
    • 2020-11-13
    • 2022-06-21
    相关资源
    最近更新 更多