【问题标题】:What is the best way to determine if a variable is a number in Ruby? [duplicate]确定变量是否是Ruby中的数字的最佳方法是什么? [复制]
【发布时间】:2012-10-30 23:36:56
【问题描述】:

可能重复:
Test if string is a number in Ruby on Rails

目前我有这个(可怕的)代码:

def is_num(num_given)
  begin
    num_given.to_i
    worked = true
  rescue
    worked = false
  ensure
    return worked
  end
end

我对此进行了重构:

def is_num(num_given)
  num_given.to_i.is_a?(Numeric) rescue false
end

这对我来说仍然感觉不对,有更好的方法吗?

这两种实现都适合我的目的,我只是在寻找一些代码兴奋。

【问题讨论】:

  • 你的函数不起作用:is_num("a") #=> true
  • 这很好,@shioyama。如果没有找到数字,则字符串上的 #to_i 返回 0,因此该函数将始终返回 true。
  • 这就是为什么……您应该始终编写并包含单元测试!

标签: ruby ruby-on-rails-3


【解决方案1】:

something.is_a?(Numeric) 是要走的路。参考您的后一个示例,无需在输入上调用 to_i。

请注意,如果您要查看字符串是否为数字,something.is_a?(Numeric) 将不起作用...

【讨论】:

  • 这不适用于字符串,我相信这是提问者想要达到的目的。
  • 恕我直言,不需要is_num? 方法。重构应该使用简单的is_a?(Numeric) 删除所有出现的事件。简洁明了。
  • @jmdeldin 是的 "string".is_a?(Numeric) #false
  • @Krule:OP 的原始代码为is_num("123") == true。 "123".is_a?(Numeric) 是假的。
  • @rthbound:错过了一个案例:is_num?("123.asd") #=> true。我在下面有一些单元测试你可以抓住。
【解决方案2】:

这是另一个解决方案。它不是很像 Ruby,但这是故意的(例如,在这种情况下,while 比 str.chars.each 快)。

# is a character between 0 and 9? (based on C's isdigit())
def digit?(c)
  o = c.ord
  o >= 48 && o <= 57 # '0'.ord, '9'.ord
end

# is a string numeric (i.e., represented as an integer or decimal)?
def numeric?(str)
  str = str.to_s unless str.is_a?(String)
  l = str.length
  i = 0

  while i < l
    c = str[i]
    if c == '.' || c == '-'
      i += 1
      next
    end

    return false if !digit?(c)

    i += 1
  end

  true
end

这里是单元测试。如果我错过了一个案例,请告诉我。对于其他回答者,只需将 subject 块更改为您的函数。

if $0 == __FILE__
  require 'minitest/autorun'
  describe :digit? do
    %w(- + : ? ! / \ ! @ $ ^ & *).each do |c|
      it "flunks #{c}" do
        digit?(c).must_equal false
      end
    end

    %w(0 1 2 3 4 5 6 7 8 9).each do |c|
      it "passes #{c}" do
        digit?(c).must_equal true
      end
    end
  end

  describe :numeric? do
    subject { :numeric? }
    %w(0 1 9 10 18 123.4567 -1234).each do |str|
      it "passes #{str}" do
        method(subject).call(str).must_equal true
      end
    end

    %w(-asdf 123.zzz blah).each do |str|
      it "flunks #{str}" do
        method(subject).call(str).must_equal false
      end
    end

    [-1.03, 123, 200_000].each do |num|
      it "passes #{num}" do
        method(subject).call(num).must_equal true
      end
    end
  end
end

【讨论】:

  • 这对于纯字符串输入非常有效。
  • @rthbound:是的,我在想这应该是String 上的一个方法,例如"asdf".numeric?,但我不能让自己去猴子补丁。我为非字符串添加了一行,它也不会对性能造成太大影响。谢谢!
【解决方案3】:

您列出的功能不起作用:

is_num("a") #=> true

问题在于它们不会为无效输入引发错误。你想要的是Integer,它会引发一个你可以挽救的错误:

def is_num(num_given)
  !!Integer(num_given) rescue false
end

这行得通:

irb(main):025:0> is_num("a")
=> false
irb(main):026:0> is_num(5)
=> true
irb(main):027:0> is_num((1..2))
=> false
irb(main):028:0> is_num("3")
=> true

(不过,可能有更自然的方式来做到这一点。)

【讨论】:

  • 伟大的收获,“!!”是一种我以前从未见过的非常酷的技术。
  • 刚刚在这里偶然发现了这个非常相似的答案:railsforum.com/viewtopic.php?pid=65026#p65026
  • 为了完整起见,我benchmarked这里有两个解决方案。这种方法在有效输入上速度更快,但显然会承受在无效输入上引发异常的开销。
  • 是的,但is_num_r 的行为与您预期的不同:is_num_r(-1) #=&gt; nil。
【解决方案4】:

您总是可以使用简单的正则表达式:

def is_num(num_given)
  num_given =~ /\d+(\.\d+)?/
end

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-03-11
    • 2017-08-23
    • 1970-01-01
    • 2011-02-25
    • 2023-04-01
    • 2023-03-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多