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

时间:2016-11-21 14:43:15

标签: ruby type-conversion

这基于“How to convert a String to Integer or Float”。

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

@ubuntu:~/Downloads$ sudo service dse start
Java executable not found (hint: set JAVA_HOME)
@ubuntu:~/Downloads$ echo $JAVA_HOME
/opt/jdk1.8.0_111
@ubuntu:~/Downloads$ java -version
java version "1.8.0_111"
Java(TM) SE Runtime Environment (build 1.8.0_111-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.111-b14, mixed mode)

但这种暴力方法调用看起来很脏。有更优雅的方式来解决这个问题吗?我可以检查值是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()的有效输入,以便我可以更加可控地调用这些方法?

3 个答案:

答案 0 :(得分:2)

使用尾随救援让我感到畏缩,因为它可以掩盖底层代码的问题,因为它会捕获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所指出的,上述情况并非如此。我使用内核来获取Integer()Float()Rational,因为这是他们定义的地方,但实际上Object是从内核继承的地方。

当我知道有一种间接调用方法的好方法时,有一天,但call在我的脑海中,而不是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

答案 1 :(得分:1)

由于某些原因,您希望将"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

这可以按预期工作:)

答案 2 :(得分:1)

the Tin Man's answer上捎带,可以使用模块覆盖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