如何检查值是否为Integer(),Float()或Rational()的有效输入?

这基于“ 如何将字符串转换为整数或浮点数 ”。

如果我想使用Ruby的内置转换机制将数字字符串输入转换为“最合适的类型”,我可以这样做:

def convert(input) value = Integer(input) rescue nil value ||= Float(input) rescue nil value ||= Rational(input) rescue nil value end convert('1') #=> 1 convert('1_000') #=> 1000 convert('0xff') #=> 255 convert('0.5') #=> 0.5 convert('1e2') #=> 100.0 convert('1/2') #=> (1/2) convert('foo') #=> nil 

但这种暴力方法调用看起来很脏。 有更优雅的方式来解决这个问题吗? 我可以检查值是否是Integer()Float()Rational()的有效输入,这样我可以以更加可控的方式调用这些方法吗?

使用尾随救援让我感到畏缩,因为它可以掩盖底层代码的问题,因为它捕获Exception而不是ArgumentError,这是失败的尝试转换会引发的。 这不简洁,但它处理适当的例外:

 def convert(input) value = begin Integer(input) rescue ArgumentError nil end value ||= begin Float(input) rescue ArgumentError nil end value ||= begin Rational(input) rescue ArgumentError nil end value end convert('1') # => 1 convert('1_000') # => 1000 convert('0xff') # => 255 convert('0.5') # => 0.5 convert('1e2') # => 100.0 convert('1/2') # => (1/2) convert('foo') # => nil 

在考虑了一下后,似乎可以将其干掉:

 def convert(input) [:Integer, :Float, :Rational].each do |m| begin return Kernel.method(m).call(input) rescue ArgumentError end end nil end convert('1') # => 1 convert('1_000') # => 1000 convert('0xff') # => 255 convert('0.5') # => 0.5 convert('1e2') # => 100.0 convert('1/2') # => (1/2) convert('foo') # => nil 

正如Jörn所指出的,上述情况并非如此。 我正在使用Kernel来获取Integer()Float()Rational因为这是他们定义的地方,但实际上Object是从内核inheritance的地方。

当我知道间接调用这个方法的好方法时,就是那些日子之一,但是call一直在我心中,而不是像Stephan指出的那样send 。 所以,这是一个更简洁的方法,从以下开始:

  return Object.send(m, input) 

但是,这可以减少到:

  return send(m, input) 

导致:

 def convert(input) [:Integer, :Float, :Rational].each do |m| begin return send(m, input) rescue ArgumentError end end nil end convert('1') # => 1 convert('1_000') # => 1000 convert('0xff') # => 255 convert('0.5') # => 0.5 convert('1e2') # => 100.0 convert('1/2') # => (1/2) convert('foo') # => nil 

由于某种原因,你更喜欢将"0.3"转换为3e-1而不是3/10 ,这可能会以更明确的方式完成。 毕竟,在引擎盖下,ruby解析器中有相同的识别机制:

 def convert input raise unless String === input && input[/\A_|_\z|__/].nil? input = input.strip.delete('_') case input when /\A-?\d+\z/ then Integer(input) when /\A-?0x[\da-f]+\z/i then Integer(input) when /\A-?(\d*\.)?\d+(e-?\d+)?\z/i then Float(input) when /\A-?(\d*\.)?\d+(e-?\d+)?\/\d+\z/i then Rational(input) end end 

这按预期工作:)

抄袭Tin Man的答案 ,可以使用模块来覆盖Kernel的默认行为:

 module SafeConvert def Integer(*) ; super ; rescue ArgumentError ; end def Float(*) ; super ; rescue ArgumentError ; end def Rational(*) ; super ; rescue ArgumentError ; end end 

这会将代码缩短为:

 class Helper include SafeConvert def convert(input) Integer(input) || Float(input) || Rational(input) end end