2010-01-11 66 views
4

我偶尔遇到过形式为Array(value),String(value)和Integer(value)的转换。在我看来,这些只是调用相应的value.to_a,value.to_s或value.to_i方法的语法糖。Ruby Integer(),Array()等 - 它们是什么?他们来自哪里?

所以我想知道:

  • 在哪里/如何在这些被定义?我无法在对象,模块,类等中找到它们。
  • 是否有任何常见的方案优先使用这些方法,而不是相应的/底层的to_X方法?
  • 这些可以在类型泛型强制中使用吗?也就是说,我可以做沿着

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

线的东西? (...并且不,我真的不想这么做;我知道我想要的类型,但我希望避免这种情况声明。)

+0

我不做太多的红宝石,但我怀疑这是铸造的语法。 – 2010-01-11 00:59:02

+0

Ruby中没有“铸造”的东西。事实上,铸造的整个思想只有C这样的弱类型语言才有意义,但是Ruby是强类型的。这些只是像其他方法一样的方法。 – 2010-01-11 01:48:59

回答

9

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

第一部分

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

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

这告诉你这些都是在内核中定义的,因此随处可见,而无需显式的前缀。

第二部分

Array()String(),...是转换方法。调用obj.to_a将返回一个数组,但如果obj不是respond_to? :to_a,将会引发NoMethodError。因此,当您偏好使用Array(),String()而不是to_ato_s时,典型情况是当您不是正面时,对象会响应给定的转换方法。如果obj不是respond_to? :to_s,则返回nil。 String(obj)还会检查to_s的结果实际上是一个字符串;它应该是,但也许是一个过于富有创造力的程序员决定返还其他东西?

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

1.9还有另一个重要的方法来转换对象(即将到来的1.8.8):Array.try_convert(obj)。如果obj不是respond_to? :to_ary,则返回nil。它不会拨打to_a。虽然它们的输入时间较长,但在编写可能接受不同类型对象的非常规代码时,您可能更喜欢使用它们,例如(因为Hashto_a方法但不包含to_ary) 。当你的方法需要一个类似数组的对象,并且你愿意做一个明确的转换时,那么obj.to_a没问题。 Array(obj)的典型用法是接受要执行的单个obj或对象列表(尽管通常将其写为[*obj])。

最后一部分

希望的答案,前两个部分给你最后的答案......

您可以使用:

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

[:Integer, :String, :Array].each{|method| send(method, obj)} 
+0

感谢您的回答。在这个和DigitalRoss的答案之间,我学到了我需要的东西以及更多。 – dondo 2010-01-12 19:07:51

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 

所以,它看起来像这些都是内核模块被混入到对象的方法,所以他们都可以不指定接收器。我们可能还需要在C实现的偷看,在object.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

+1感谢您展示您如何发现它。这将在未来的项目中非常有用! – 2010-01-11 01:10:57

+0

您的回答很接近,但我发现它们都不正确(.to_a不被弃用!)并且不完整。在下面检查我的回复。 – 2010-01-11 16:47:01

+0

啊,对不起,你没有意识到你正在谈论默认的Object#to_a,这在1.9中是正确的。所以你的答案是接近(r),但仍然不完整;-)如果定义了一个数组,则Array(x)将调用to_ary。我认为隐式转换(to_ary)和显式转换(to_a)的概念既微妙又重要。 – 2010-01-11 20:47:37

0

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

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

我发现这些方法的引用in Dave Thomas's Pickaxe book "Progamming Ruby 1.9“,第555页。

例如:Array(arg)会将arg转换为Array,以下内容将从book: “将arg作为数组返回。首先尝试调用rg.to_ary,然后调用arg.to_a。如果都失败,创建一个包含ARG的单一元素的数组(如果arg是零空数组)。” 前。

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

0

据我所知,简单版本是l ike this:

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

我可以重新定义什么.to_a意味着给定的类(它只是另一个成员)。内核中定义了Array(...)调用,因此它对任何类都表现相同。我通常使用Array(...)类型的类型转换,当我不知道什么时候会传入什么类型的对象。最好是处理一个对象不知道如何将其自身转换为数组或不能被转换为数组。如果要转换的对象是长或复杂表达式的结果,则使用Array(...)样式通常更清晰。当我知道对象的类别以及.to_a的输出(主要是我自己编写或修改.to_a成员函数时的实例)的输出时,我将.to_a表格保存为实例。

相关问题