既然Ruby做类型转换,我该如何正确获取索引?
我希望这个能退回1个
[1,2.0,2,3].index(2.0)
#=> 1
我希望这个能退回2个
[1,2.0,2,3].index(2)
#=> 1
将块与eql?
一起使用是一种方法:
[1,2.0,2,3].index {|e| e.eql? 2.0}
#=> 1
[1,2.0,2,3].index {|e| e.eql? 2}
#=> 2
与==
不同,只有当接收器和自变量具有相同类型和相等值时,eql?
才会返回true
。所以2 == 2.0
返回true
,而2.eql? 2.0
返回false
。
数组#索引和相等
您没有得到预期的结果,因为Array#索引使用了更通用的BasicObject#==而不是Object#eql?以比较值,而不考虑参数/值的类型。在鸭子打字语言中,这通常是你想要的。
考虑以下示例,该示例使用==
将Float与Fixnum进行比较:
2 == 2.0
#=> true
注意,Ruby认为这两个数值是相等的,尽管它们的类型不同。这是记录在案的行为。由于数组#索引的非块形式将参数为==
的第一个索引返回到索引值,因此[1,2.0,2,3].index(2.0)
和[1,2.0,2,3].index(2)
都将返回相同的索引。
使用数组#索引的块形式
所有Ruby方法都接受一个可选的块,当Kernel#block_given?是真的。Array#索引的文档中写道:
如果给定一个块。。。返回块返回true的第一个对象的索引。如果未找到匹配项,则返回nil。
区分两种不同类型值的规范方法是使用数组#索引的块形式来检查对象是否与#eql相等?而不是使用#==。例如:
array = [1,2.0,2,3]
array.index { |i| i.eql? 2 }
array.index { |i| i.eql? 2.0 }
这会返回您期望的值,但需要额外键入一些内容。这确实是解决你问题的首选方案。
猴子补阵类
猴子修补像Array这样的核心类通常是个坏主意™,但是,您可以通过重新打开Array类并修改Array#index的行为来检查类型和值是否相等,从而强制Array#index。一种方法是借助Module#alias_method,并使用Array#old_index的块语法检查Numeric#eql?无论何时使用数字参数调用数组#索引。
考虑以下内容:
class Array
alias_method :old_index, :index
def index value
old_index { |i| i.eql? value }
end
end
[1,2.0,2,3].index 2.0
#=> 1
[1,2.0,2,3].index 2
#=> 2
这按照您预期的方式工作,并且您仍然可以在任何时候使用Array#old_index进行原始类型不可知的相等性检查。但是,请小心使用,因为一旦您更改了Array类的正常行为,其他模块或gem可能不会按预期运行。
很高兴知道你可以这样做,但玩电锯是一项固有的风险活动。您的里程数可能有所不同。