Ruby Integer(),Array()等等 - 它们是什么?他们来自哪里?

时间:2010-01-11 00:54:59

标签: ruby-on-rails ruby

我偶尔会遇到表单Array(value),String(value)和Integer(value)的转换。在我看来,这些只是调用相应的value.to_a,value.to_s或value.to_i方法的语法糖。

所以我想知道:

  • 这些是何种/如何定义?我无法在对象,模块,类等中找到它们
  • 是否有任何常见的场景,最好使用这些场景而不是相应的/底层的to_X方法?
  • 这些可以用于类型通用强制吗?也就是说,我可以按照

    的方式做点什么
    [Integer, String, Array].each {|klass| klass.do_generic_coercion(foo) }
    

? (...和不,我真的不想这样做;我知道我想要的类型,但我希望避免使用案例陈述。)

4 个答案:

答案 0 :(得分:10)

这是一个好而难的问题。让我们回答这三个部分。

第一部分

要找到定义,重要的是要意识到方法的名称是“数组”等,这可能非常违反直觉,因为方法通常是小写的......

irb> method(:Array)
=> #<Method: Object(Kernel)#Array>

这告诉你这些是在内核中定义的,因此可以在任何地方使用,而不需要显式的前缀。

第二部分

Array()String(),...是转换方法。调用obj.to_a将返回一个数组,但如果obj没有NoMethodError,则会引发respond_to? :to_a。因此,当您偏好使用Array()String()而不是to_ato_s时,典型情况是当您不肯定时,对象会响应给定的转换方法。

如果obj不String(obj)

nil将返回respond_to? :to_s。 String(obj)还将检查to_s的结果实际上是否为字符串;它应该是,但也许一个过于有创造力的程序员决定返回别的东西?

大多数其他转换方法的行为方式相同,但Array(obj)不同。如果obj不[obj],它将返回respond_to? :to_a。它实际上会调用to_ary(这是隐式转换操作,而to_a是显式转换操作。)

还有另一种重要的方法来转换1.9(和即将发布的1.8.8)中的对象:Array.try_convert(obj)。如果obj不nil,则返回respond_to? :to_ary。它不会调用to_a。虽然它们的输入时间较长,但您可能更喜欢在编写可能接受不同类型对象的非常通用代码时使用它们,并且希望避免错误地将哈希转换为数组(例如Hash具有{{ 1}}方法,但不是to_a)。当您的方法需要类似数组的对象并且您愿意进行显式转换时,to_ary就可以了。 obj.to_a的典型用法是在接受单个Array(obj)操作的方法或对象列表中(尽管通常将其写为obj)。

最后一部分

希望前两部分的答案能为您提供最终答案......

您可以使用:

[*obj]

[Integer, String, Array].each {|klass| klass.try_convert(foo) }

答案 1 :(得分:8)

好问题!让我们看看我们是否可以搞清楚。

Ross-Harveys-MacBook-Pro:ruby-1.9.1-p376 ross$ irb
irb(main):001:0> Object.ancestors
=> [Object, Kernel]
irb(main):002:0> Kernel.ancestors
=> [Kernel]
irb(main):003:0> Kernel.class
=> Module
irb(main):004:0> Kernel.public_methods.include? "Array"
=> true

因此,看起来这些是内核模块中混合到Object中的方法,因此可以在不指定接收器的情况下使用它们。我们可能还想在object.c中查看C实现:

VALUE
rb_Array(VALUE val)
{
    VALUE tmp = rb_check_array_type(val);

    if (NIL_P(tmp)) {
        tmp = rb_check_convert_type(val, T_ARRAY, "Array", "to_a");
        if (NIL_P(tmp)) {
            return rb_ary_new3(1, val);
        }
    }
    return tmp;
}

有一点似乎很容易得出结论,不推荐使用默认的.to_a,因此Array(x)似乎是执行转换的规范方式。如果给出一个数组,它显然什么都不做,如果存在则调用.to_a,如果没有,它只是将其参数包装在数组中。

关于to_a是否已被弃用......好吧,我说“默认”:

Ross-Harveys-MacBook-Pro:puppet_sd ross$ irb
irb(main):001:0> class X; X; end.new.to_a
(irb):1: warning: default `to_a' will be obsolete

答案 2 :(得分:0)

它们在Ruby内核模块中定义,如:

Array(), Complex(), Float(), Integer(), Rational(), Stirng(), etc.

我在Dave Thomas的Pickaxe书"Progamming Ruby 1.9中找到了这些方法参考,第555页。

例如:Array(arg)会将arg转换为数组,以下内容将从本书中复制: “返回arg作为数组。首先尝试调用rg.to_ary,然后调用arg.to_a。如果两者都失败,则创建一个包含arg的单个元素数组(如果arg为nil,则创建一个空数组)。”    恩。

 Array(1..5)   # => [1, 2, 3, 4, 5] 

答案 3 :(得分:0)

据我所知,简单版本是这样的:

  • object.to_a尝试使用类成员函数将“对象”转换为数组。
  • Array(object)尝试使用'object'创建一个新数组。

我可以重新定义.to_a对于给定类的含义(毕竟它只是另一个成员)。 Array(...)调用在内核中定义,因此对于任何类,它的行为都相同。我通常使用样式Array(...)的类型转换,因为我事先不知道将传入什么类型的对象。它更好地处理对象不知道如何将自身转换为数组的情况或者无法转换为数组。如果要转换的对象是长表达式或复杂表达式的结果,则使用Array(...)样式通常更清晰。当我知道对象的类以及从.to_a的输出中得到什么时,我保存.to_a形式的实例(大部分是我编写或修改.to_a成员函数时的实例自己)。