【问题标题】:Is ruby's multidimensional array out of bounds behaviour consistent?ruby 的多维数组越界行为是否一致?
【发布时间】:2012-04-03 18:00:06
【问题描述】:

如果我有一个多维数组,我可以超出最终维度的边界并返回 nil,但如果我超出了非最终维度的边界,则会收到错误消息。这是设计使然吗?如果是,原因是什么?

> ar = [ [00,01,02], [10,11,12], [20,21,22] ]
=> [[0, 1, 2], [10, 11, 12], [20, 21, 22]]
> ar[2][2]
=> 22
> ar[2][3]
=> nil
> ar[3][2]
NoMethodError: undefined method `[]' for nil:NilClass
from (irb):32
from :0

我明白为什么会发生这种情况,但为什么没有将 nil[] 定义为返回 nil?

【问题讨论】:

    标签: ruby arrays multidimensional-array


    【解决方案1】:

    在 Ruby 中,没有多维数组。你有一个数组,包含数组作为元素。所以如果你得到第一个“维度”,你会得到另一个数组(如果你超出了外部数组的边界,则返回 nil)。

    nilNilClass 的一个对象,它具有一组有限(且很小)的已定义方法。而[] 方法,在您使用whatever[:foo] 语法时调用,只是没有在NilClass 上定义。因此它不能返回任何东西。

    一般来说,在 nil 上定义所有可能的方法是没有意义的,因为它会使人们更加困惑,并且会引入大量难以检测的错误。

    但是,如果您知道自己在做什么并且愿意处理其中的含义,则可以使用由某些框架(例如 ActiveSupport for Rails)定义但不是 Ruby 本身的一部分的 try 方法。它捕获NoMethodError 并返回nil。在您的情况下,您可以使用

    > ar[2].try(:[], 2)
    => nil
    

    但是,通常不鼓励这样做,因为它会使调试变得更加困难。相反,您应该在尝试访问数组之前检查边界(例如,通过使用 array.length)或使用包含循环结构,如 ar.each {|e| puts e}

    【讨论】:

    • 感谢您的回复。正如我所提到的,我确实理解上面的示例中发生了什么,我只是觉得这是一个奇怪的设计选择。我确实认为,正如您所提到的,由于数组不是真正的多维数组,而只是嵌套对象,因此要使行为保持一致,确实会使事情复杂化。
    【解决方案2】:

    我不知道Matz 是否记录了为什么NilClass 是这样设计的。如果不是这样,那么我们只能猜测。我的猜测是它基于 Smalltalk 的行为。

    nil 可以是消息吃异常抛出。 Ruby 有一个抛出 nil 对象的异常。

    如果你有一个消息吃 nil,那么很难确定在像arr[1][2][3] 这样的链式调用中第一个返回nil 的对象。我不知道这真的是一个问题的频率,但这似乎是一个有效的观点。作为一个反例,Objective-C 似乎可以很好地处理 消息吃 nil。

    你可以给NilClass打补丁变成消息吃

    class NilClass
      def method_missing(*)
        nil
      end
    end
    

    或仅用于数组

    class NilClass
      def []
        nil
      end
    end
    

    两者都使 nil[] 返回 nil 并且都可以破坏现有代码

    nil[][][][]
    => nil
    

    【讨论】:

    • 感谢您的深思!正如您所提到的,我绝对考虑过修补NilClass。这将给出一致的行为。在我看来,另一种选择是修补 Array 以抛出 OutOfBoundsException,但我可以看到这会产生令人困惑的结果,因为 ruby​​ 数组非常灵活。
    • 我知道我之前读过 Ruby 中的空对象模式,但我不记得是 @Avid 写的。那里有一些非常好的建议!
    • 噢!我只是不小心删除了我之前对Null Object Pattern 的评论。此外,还有一篇关于message eating vs. exception throwing nil here. 的文章。
    猜你喜欢
    • 1970-01-01
    • 2018-06-21
    • 1970-01-01
    • 2021-12-30
    • 2018-02-25
    • 2014-03-04
    • 2013-12-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多