ho visto la discussione irrisolta su calco + eccezioni vs regex e io ho pensato di provare a punto di riferimento di tutto e di produrre una risposta obiettiva:
Ecco la sorgente per il caso migliore e peggiore di ciascun metodo tentato qui:
require "benchmark"
n = 500000
def is_float?(fl)
!!Float(fl) rescue false
end
def is_float_reg(fl)
fl =~ /(^(\d+)(\.)?(\d+)?)|(^(\d+)?(\.)(\d+))/
end
class String
def to_float
Float self rescue (0.0/0.0)
end
end
Benchmark.bm(7) do |x|
x.report("Using cast best case") {
n.times do |i|
temp_fl = "#{i + 0.5}"
is_float?(temp_fl)
end
}
x.report("Using cast worst case") {
n.times do |i|
temp_fl = "asdf#{i + 0.5}"
is_float?(temp_fl)
end
}
x.report("Using cast2 best case") {
n.times do |i|
"#{i + 0.5}".to_float
end
}
x.report("Using cast2 worst case") {
n.times do |i|
"asdf#{i + 0.5}".to_float
end
}
x.report("Using regexp short") {
n.times do |i|
temp_fl = "#{i + 0.5}"
is_float_reg(temp_fl)
end
}
x.report("Using regexp long") {
n.times do |i|
temp_fl = "12340918234981234#{i + 0.5}"
is_float_reg(temp_fl)
end
}
x.report("Using regexp short fail") {
n.times do |i|
temp_fl = "asdf#{i + 0.5}"
is_float_reg(temp_fl)
end
}
x.report("Using regexp long fail") {
n.times do |i|
temp_fl = "12340918234981234#{i + 0.5}asdf"
is_float_reg(temp_fl)
end
}
end
con i seguenti risultati con mri193:
user system total real
Using cast best case 0.608000 0.000000 0.608000 ( 0.615000)
Using cast worst case 5.647000 0.094000 5.741000 ( 5.745000)
Using cast2 best case 0.593000 0.000000 0.593000 ( 0.586000)
Using cast2 worst case 5.788000 0.047000 5.835000 ( 5.839000)
Using regexp short 0.951000 0.000000 0.951000 ( 0.952000)
Using regexp long 1.217000 0.000000 1.217000 ( 1.214000)
Using regexp short fail 1.201000 0.000000 1.201000 ( 1.202000)
Using regexp long fail 1.295000 0.000000 1.295000 ( 1.284000)
Dato che ci occupiamo solo di algoritmi di tempo lineare, penso che usiamo misurazioni empiriche per effettuare generalizzazioni. È chiaro che la regex è più coerente e fluttuerà solo un bit in base alla lunghezza della stringa passata. Il cast è chiaramente più veloce quando non ci sono errori e molto più lentamente quando ci sono dei fallimenti.
Se confrontiamo i tempi di successo possiamo vedere che il cast best case è di circa 3 secondi più veloce rispetto al regex best case. Se dividiamo questo per la quantità di tempo nel caso peggiore, possiamo stimare il numero di esecuzioni che occorrerebbe per pareggiare con eccezioni rallentando il lancio verso il basso per far corrispondere le velocità di regex. Circa 6 secondi immessi da .3 ci danno circa 20. Quindi, se le prestazioni sono importanti e ci si aspetta che meno di 1 su 20 del test fallisca, utilizzare le eccezioni cast +.
JRuby 1.7.4 ha risultati completamente diversi:
user system total real
Using cast best case 2.575000 0.000000 2.575000 ( 2.575000)
Using cast worst case 53.260000 0.000000 53.260000 (53.260000)
Using cast2 best case 2.375000 0.000000 2.375000 ( 2.375000)
Using cast2 worst case 53.822000 0.000000 53.822000 (53.822000)
Using regexp short 2.637000 0.000000 2.637000 ( 2.637000)
Using regexp long 3.395000 0.000000 3.395000 ( 3.396000)
Using regexp short fail 3.072000 0.000000 3.072000 ( 3.073000)
Using regexp long fail 3.375000 0.000000 3.375000 ( 3.374000)
Cast è solo marginalmente più veloce nel migliore dei casi (circa il 10%). Presumendo che questa differenza sia appropriata per fare generalizzazioni (non credo che lo sia), allora il break even point è tra 200 e 250 run con solo 1 che causa un'eccezione.
Quindi le eccezioni devono essere utilizzate solo quando si verificano cose veramente eccezionali, questa è una decisione per te e per il tuo codice base. Quando non vengono utilizzati, il codice in cui sono inseriti può essere più semplice e veloce.
Se le prestazioni non sono importanti, probabilmente dovresti solo seguire le convenzioni del tuo team o della base di codice e ignorare tutte le risposte.
Ottima risposta! Nota: questa regex si è evoluta un po 'nell'implementazione di Rubinius, per i dettagli vedere le specifiche su https://github.com/rubinius/rubinius/blob/master/spec/ruby/core/string/to_f_spec.rb. Tieni presente inoltre che se stai usando questo per convalidare l'input dell'utente, potresti voler escludere il supporto per i trattini bassi e usare solo la regex di Rubinius per l'ispirazione :) Correzione – captainpete
, Rubinius usa ancora la stessa espressione regolare per Float(). Trova il codice su https://github.com/rubinius/rubinius/blob/master/kernel/common/kernel19.rb – captainpete