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

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

所以我想知道:

  • 这些是何种/如何定义? 我无法在Object,Module,Class等中找到它们
  • 是否有任何常见的情况,最好使用这些而不是相应的/底层的to_X方法?
  • 这些可以用于类型通用强制吗? 也就是说,我可以做一些事情

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

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

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

第一部分

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

 irb> method(:Array) => # 

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

第二部分

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

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

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

还有另一种重要的方法来转换1.9(和即将发布的1.8.8)中的对象: Array.try_convert(obj) 。 如果obj没有respond_to? :to_ary则返回nil respond_to? :to_ary respond_to? :to_ary 。 它不会调用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)} 

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

 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 

它们在Ruby Kernel Module中定义,如:

  Array(),Complex(),Float(),Integer(),Rational(),Stirng()等。 

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

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

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

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

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

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