2011-11-09 4 views
5

私はこの問題に対する良い答えがあると信じていますが、これを行うにはルビーファイルにはもっと良い方法がないことを確認したかったのです。入力値をIntegerまたはFloatに適切に変換する

基本的に、入力文字列を指定すると、文字列を適切な場合は整数に、必要に応じて浮動小数点に変換したいと考えています。それ以外の場合は、文字列を返します。

以下に私の回答を掲載しますが、より良い方法があるかどうかを知りたいと思います。

例:

to_f_or_i_or_s("0523.49") #=> 523.49 
to_f_or_i_or_s("0000029") #=> 29 
to_f_or_i_or_s("kittens") #=> "kittens" 

答えて

9

できるだけRubyでregexを使用しないでください。それは悪名高く遅いです。

def to_f_or_i_or_s(v) 
    ((float = Float(v)) && (float % 1.0 == 0) ? float.to_i : float) rescue v 
end 

# Proof of Ruby's slow regex 
def regex_float_detection(input) 
    input.match('\.') 
end 

def math_float_detection(input) 
    input % 1.0 == 0 
end 

n = 100_000 
Benchmark.bm(30) do |x| 
    x.report("Regex") { n.times { regex_float_detection("1.1") } } 
    x.report("Math") { n.times { math_float_detection(1.1) } } 
end 

#          user  system  total  real 
# Regex       0.180000 0.000000 0.180000 ( 0.181268) 
# Math       0.050000 0.000000 0.050000 ( 0.048692) 

より包括的なベンチマーク:

def wattsinabox(input) 
    input.match('\.').nil? ? Integer(input) : Float(input) rescue input.to_s 
end 

def jaredonline(input) 
    ((float = Float(input)) && (float % 1.0 == 0) ? float.to_i : float) rescue input 
end 

def muistooshort(input) 
    case(input) 
    when /\A\s*[+-]?\d+\.\d+\z/ 
     input.to_f 
    when /\A\s*[+-]?\d+(\.\d+)?[eE]\d+\z/ 
     input.to_f 
    when /\A\s*[+-]?\d+\z/ 
     input.to_i  
    else 
     input 
    end 
end 

n = 1_000_000 
Benchmark.bm(30) do |x| 
    x.report("wattsinabox") { n.times { wattsinabox("1.1") } } 
    x.report("jaredonline") { n.times { jaredonline("1.1") } } 
    x.report("muistooshort") { n.times { muistooshort("1.1") } } 
end 

#          user  system  total  real 
# wattsinabox      3.600000 0.020000 3.620000 ( 3.647055) 
# jaredonline      1.400000 0.000000 1.400000 ( 1.413660) 
# muistooshort     2.790000 0.010000 2.800000 ( 2.803939) 
5
def to_f_or_i_or_s(v) 
    v.match('\.').nil? ? Integer(v) : Float(v) rescue v.to_s 
end 
2

あなたは(String#to_fはありません)科学表記法で数字を処理したい場合は正規表現の山は良いアイデアかもしれません:

def to_f_or_i_or_s(v) 
    case(v) 
    when /\A\s*[+-]?\d+\.\d+\z/ 
     v.to_f 
    when /\A\s*[+-]?\d+(\.\d+)?[eE]\d+\z/ 
     v.to_f 
    when /\A\s*[+-]?\d+\z/ 
     v.to_i  
    else 
     v 
    end 
end 

あなたがマッシュ可能性あなたが望むならば、両方ともto_fケースを1つの正規表現に入れます。

小数点記号としてカンマを使用するロケールで'3,14159'を入力すると、これはもちろん失敗します。

+0

私の関数では、実際には文字列として返されます。なぜなら、浮動小数点数の変換は失敗し、例外がスローされ、レスキューがto_sを実行して復帰するからです。 – WattsInABox

+0

@WattsInABox:そうです(私が「Float」をどのくらい使っているかを示しています)。しかし、あなたはまだ心配する科学的表記法を持っています。 –

+0

これは本当に@muistooshortです。ありがとう! – WattsInABox

2

は、セキュリティ要件に依存します。

def to_f_or_i_or_s s 
    eval(s) rescue s 
end 
0

私は

def to_f_or_i_or_s(value) 
    return value if value[/[a-zA-Z]/] 

    i = value.to_i 
    f = value.to_f 

    i == f ? i : f 
    end 
0

この方法を使用しCSVこれを行うコンバータを持っています。

require "csv" 
strings = ["0523.49", "29","kittens"] 
strings.each{|s|p s.parse_csv(converters: :numeric).first} 

#523.49 
#29 
#"kittens" 

しかし、何らかの理由で「00029」をフロートに変換します。

関連する問題