ruby-on-rails - conversiones - entero en ruby
Determine si una cadena es un valor flotante vĂ¡lido (8)
¿Hay una manera de verificar simplemente si un valor de cadena es un valor flotante válido. Llamar a to_f en una cadena lo convertirá a 0.0 si no es un valor numérico. Y usar Float () genera una excepción cuando se pasa una cadena float no válida que está más cerca de lo que quiero, pero no quiero manejar las excepciones de captura. Lo que realmente quiero es un método como nan? que existe en la clase Float, pero eso no ayuda porque una cadena no numérica no se puede convertir en un float sin cambiar a 0.0 (usando to_f).
"a".to_f => 0.0
"a".to_f.nan? => false
Float("a") => ArgumentError: invalid value for Float(): "a"
¿Existe una solución simple para esto o necesito escribir código para verificar si una cadena es un valor flotante válido?
Aquí hay una forma:
class String
def valid_float?
# The double negation turns this into an actual boolean true - if you''re
# okay with "truthy" values (like 0.0), you can remove it.
!!Float(self) rescue false
end
end
"a".valid_float? #false
"2.4".valid_float? #true
Si quieres evitar el parche de cadena de la cadena, siempre puedes hacer de este un método de clase de algún módulo que controles, por supuesto:
module MyUtils
def self.valid_float?(str)
!!Float(str) rescue false
end
end
MyUtils.valid_float?("a") #false
Intenté agregar esto como un comentario pero aparentemente no hay formato en los comentarios:
por otro lado, ¿por qué no usar eso como su función de conversión, como
class String
def to_float
Float self rescue (0.0 / 0.0)
end
end
"a".to_float.nan? => true
que por supuesto es lo que no querías hacer en primer lugar. Supongo que la respuesta es: "tienes que escribir tu propia función si realmente no quieres usar el manejo de excepciones, pero, ¿por qué harías eso?"
Prueba esto
def is_float(val)
fval = !!Float(val) rescue false
# if val is "1.50" for instance
# we need to lop off the trailing 0(s) with gsub else no match
return fval && Float(val).to_s == val.to_s.gsub(/0+$/,'''') ? true:false
end
s = "1000"
is_float s
=> false
s = "1.5"
is_float s
=> true
s = "Bob"
is_float s
=> false
n = 1000
is_float n
=> false
n = 1.5
is_float n
=> true
Umm, si no quieres excepciones, entonces quizás:
def is_float?(fl) fl =~ /(^(/d+)(/.)?(/d+)?)|(^(/d+)?(/.)(/d+))/ end
Desde OP solicitó específicamente una solución sin excepciones. La solución basada en Regexp es ligeramente lenta:
require "benchmark" n = 500000 def is_float?(fl) !!Float(fl) rescue false end def is_float_reg(fl) fl =~ /(^(/d+)(/.)?(/d+)?)|(^(/d+)?(/.)(/d+))/ end Benchmark.bm(7) do |x| x.report("Using cast") { n.times do |i| temp_fl = "#{i + 0.5}" is_float?(temp_fl) end } x.report("using regexp") { n.times do |i| temp_fl = "#{i + 0.5}" is_float_reg(temp_fl) end } end
Resultados:
5286 snippets:master!? % user system total real Using cast 3.000000 0.000000 3.000000 ( 3.010926) using regexp 5.020000 0.000000 5.020000 ( 5.021762)
Un hecho interesante sobre el mundo de Ruby es la existencia del proyecto Rubinius, que implementa Ruby y su biblioteca estándar principalmente en Ruby puro. Como resultado, tienen una implementación pura de Ruby de Kernel # Float, que se parece a:
def Float(obj)
raise TypeError, "can''t convert nil into Float" if obj.nil?
if obj.is_a?(String)
if obj !~ /^/s*[+-]?((/d+_?)*/d+(/.(/d+_?)*/d+)?|/.(/d+_?)*/d+)(/s*|([eE][+-]?(/d+_?)*/d+)/s*)$/
raise ArgumentError, "invalid value for Float(): #{obj.inspect}"
end
end
Type.coerce_to(obj, Float, :to_f)
end
Esto le proporciona una expresión regular que coincide con el trabajo interno que realiza Ruby cuando ejecuta Float (), pero sin la excepción. Así que ahora podrías hacer:
class String
def nan?
self !~ /^/s*[+-]?((/d+_?)*/d+(/.(/d+_?)*/d+)?|/.(/d+_?)*/d+)(/s*|([eE][+-]?(/d+_?)*/d+)/s*)$/
end
end
Lo bueno de esta solución es que, dado que Rubinius se ejecuta y pasa RubySpec, sabes que esta expresión regular maneja los casos de borde que el mismo Ruby maneja, ¡y puedes llamar a to_f en el String sin ningún temor!
Vi la discusión no resuelta sobre cast + exceptions vs regex y pensé que intentaría comparar todo y producir una respuesta objetiva:
Aquí está la fuente del mejor caso y el peor de cada método que se intenta aquí:
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 los siguientes resultados 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)
Ya que estamos tratando solo con algoritmos de tiempo lineal, creo que usamos medidas empíricas para hacer generalizaciones. Es fácil ver que la expresión regular es más consistente y solo fluctuará un poco en función de la longitud de la cadena pasada. El reparto es claramente más rápido cuando no hay fallas, y mucho más lento cuando hay fallas.
Si comparamos los tiempos de éxito, podemos ver que el mejor caso de reparto es aproximadamente .3 segundos más rápido que el mejor caso de expresiones regulares. Si dividimos esto por la cantidad de tiempo en el peor de los casos, podemos estimar cuántas ejecuciones se necesitaría para compensar, con la excepción de la disminución de la conversión para coincidir con las velocidades de expresión regular. Unos 6 segundos divididos por .3 nos dan aproximadamente 20. Por lo tanto, si el rendimiento importa y usted espera que menos de 1 de cada 20 de sus pruebas falle, entonces use cast + excepciones.
JRuby 1.7.4 tiene resultados completamente diferentes:
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)
El lanzamiento es solo ligeramente más rápido en el mejor de los casos (alrededor del 10%). Suponiendo que esta diferencia es apropiada para hacer generalizaciones (no creo que lo sea), entonces el punto de equilibrio está en algún lugar entre 200 y 250 carreras con solo 1 causando una excepción.
Por lo tanto, las excepciones solo deben usarse cuando suceden cosas verdaderamente excepcionales, esta es una decisión para usted y su base de código. Cuando no se usan, el código en el que están puede ser más simple y más rápido.
Si el rendimiento no importa, probablemente debería seguir las convenciones que su equipo o base de código ya tiene e ignorar todas estas respuestas.
# Edge Cases:
# numeric?"Infinity" => true is_numeric?"Infinity" => false
def numeric?(object)
true if Float(object) rescue false
end
#Possibly faster alternative
def is_numeric?(i)
i.to_i.to_s == i || i.to_f.to_s == i
end
def float?(string)
true if Float(string) rescue false
end
Esto admite 1.5
, 5
, 123.456
, 1_000
pero no 1 000
, 1,000
, etc. (por ejemplo, igual que String#to_f
).
>> float?("1.2")
=> true
>> float?("1")
=> true
>> float?("1 000")
=> false
>> float?("abc")
=> false
>> float?("1_000")
=> true
Fuente: https://github.com/ruby/ruby/blob/trunk/object.c#L2934-L2959