2011-08-14 11 views
8

He estado participando en a programming contest y one of the problems 'datos de entrada incluyen un número fraccionario en formato decimal: 0.75 es un ejemplo.Cómo analizar una fracción decimal en Rational en Haskell?

Analizando eso en Double es trivial (puedo usar read para eso), pero la pérdida de precisión es dolorosa. Hay que tener mucho cuidado con las comparaciones Double (no lo estaba), lo que parece redundante ya que uno tiene el tipo de datos Rational en Haskell.

Al intentar utilizar eso, he descubierto que a read un Rational uno tiene que proporcionar una cadena con el siguiente formato: numerator % denominator, lo cual, obviamente, no tengo.

Entonces, la pregunta es:

¿Cuál es la forma más fácil de analizar una representación decimal de una fracción en Rational?

El número de dependencias externas también debe tenerse en cuenta, ya que no puedo instalar bibliotecas adicionales en el juez en línea.

Respuesta

15

La función que desea es Numeric.readFloat:

Numeric Data.Ratio> fst . head $ readFloat "0.75" :: Rational 
3 % 4 
+0

Gracias! Esto funciona. – Rotsor

+6

es posible que desee agregar 'readSigned' si desea poder leer los números negativos:' fst. head $ readSigned readFloat "-3.14" :: Rational' – newacct

3

¿Qué hay de lo siguiente (sesión GHCi):

> :m + Data.Ratio 
> approxRational (read "0.1" :: Double) 0.01 
1 % 10 

Por supuesto, usted tiene que escoger su épsilon adecuadamente.

+0

¡Esta es una buena idea! ¡Creo que esto debería usarse en lugar de 'toRational' en la mayoría de las circunstancias! – Rotsor

+0

Desafortunadamente, la elección de épsilon no es obvia aquí. Por ejemplo, 'approxRational 0.999 0.0001' es' 909% 910', que no es lo que quiero. El épsilon adecuado para usar en este caso es '0.000001' (precisión al cuadrado?) – Rotsor

1

Tal vez se obtendría puntos extra en el concurso para la implementación de usted mismo:

import Data.Ratio ((%)) 

readRational :: String -> Rational 
readRational input = read intPart % 1 + read fracPart % (10^length fracPart) 
    where (intPart, fromDot) = span (/='.') input 
     fracPart   = if null fromDot then "0" else tail fromDot 
+0

No lo creo. En tales concursos solo importa el tiempo y la exactitud de la presentación. Buena solución, lo suficientemente corta como para codificar en caso de emergencia. – Rotsor

Cuestiones relacionadas