在Ruby中,区分十进制数的索引和数组内的整数



既然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可能不会按预期运行。

很高兴知道你可以这样做,但玩电锯是一项固有的风险活动。您的里程数可能有所不同。

最新更新