2010-03-18 24 views
63
data GroceryItem = CartItem ItemName Price Quantity | StockItem ItemName Price Quantity 

makeGroceryItem :: String -> Float -> Int -> GroceryItem 
makeGroceryItem name price quantity = CartItem name price quantity 

I want to create a `GroceryItem` when using a `String` or `[String]` 

createGroceryItem :: [String] -> GroceryItem 
createGroceryItem (a:b:c) = makeGroceryItem a b c 

La entrada tendrá el formato ["Apple","15.00","5"] que corté utilizando la función words de Haskell.¿Convertir cadena a entero/flotar en Haskell?

Recibo el siguiente error que creo que es porque makeGroceryItem acepta Float y Int.

*Type error in application 
*** Expression  : makeGroceryItem a read b read c 
*** Term   : makeGroceryItem 
*** Type   : String -> Float -> Int -> GroceryItem 
*** Does not match : a -> b -> c -> d -> e -> f* 

Pero cómo hago bc y de tipo Float y Int, respectivamente?

+0

usted tiene un proyecto interesante. ¿Para qué sirve? –

Respuesta

80

read puede analizar una cadena en flotador y int:

Prelude> :set +t 
Prelude> read "123.456" :: Float 
123.456 
it :: Float 
Prelude> read "123456" :: Int 
123456 
it :: Int 

Pero el problema (1) está en su patrón:

createGroceryItem (a:b:c) = ... 

Aquí : es un (asociativo por la derecha) operador binario que precede un elemento a una lista. El RHS de un elemento debe ser una lista. Por lo tanto, dada la expresión a:b:c, Haskell inferirá los siguientes tipos:

a :: String 
b :: String 
c :: [String] 

es decir c será considerado como una lista de cadenas. Obviamente no puede ser read ni pasar a ninguna función esperando una Cadena.

lugar debe usar

createGroceryItem [a, b, c] = ... 

si la lista debe tener exactamente 3 artículos, o

createGroceryItem (a:b:c:xs) = ... 

si ≥ 3 artículos son aceptables.

También (2), la expresión

makeGroceryItem a read b read c 

se interpretará como makeGroceryItem tomando 5 argumentos, 2 de los cuales son la función read. Es necesario utilizar paréntesis:

makeGroceryItem a (read b) (read c) 
+0

@KennyTM: 'leer" 123.456 ":: Flotador'. ¿Qué significa esta sintaxis? ¿Qué es '::' aquí? ¿'Leer' es una función? – Nawaz

+0

@Nawaz: Sí 'leer' es una función. La expresión 'f :: T' obliga a' f' a tener el tipo 'T'. – kennytm

+0

@KennyTM: Así que la sintaxis 'leer "123.456" :: Float' es más o menos equivalente a' sscanf ("123.456", "% f", &fnum); 'en C, a la derecha – Nawaz

5

dos cosas:

createGroceryItem [a, b, c] = makeGroceryItem a (parse b) (parse c) 
-- pattern match error if not exactly 3 items in list 

o alternativamente

createGroceryItem (a : b : c : _) = makeGroceryItem a (parse b) (parse c) 
-- pattern match error if fewer than 3 items in list, ignore excess items 

porque : no es lo mismo que ++.

Mientras tanto en el lado derecho --- el lado que le está dando el mensaje de error que ve --- tiene que agrupar expresiones usando corchetes. De lo contrario, parse se interpreta como un valor que desea pasar a makeGroceryItem, por lo que el compilador se queja cuando intenta pasar 5 argumentos a una función que solo toma 3 parámetros.

75

Aunque esta pregunta ya tiene una respuesta, sugiero usar reads para la conversión de cadenas, porque es mucho más seguro, ya que no falla con una excepción irrecuperable.

reads :: (Read a) => String -> [(a, String)] 

Prelude> reads "5" :: [(Double, String)] 
[(5.0,"")] 
Prelude> reads "5ds" :: [(Double, String)] 
[(5.0,"ds")] 
Prelude> reads "dffd" :: [(Double, String)] 
[] 

En caso de éxito reads, devuelve una lista con exactamente un elemento: Una tupla que consiste en el valor convertido y caracteres adicionales tal unconvertable. En caso de error, reads devuelve una lista vacía.

Es fácil patrón-partido en el éxito y el fracaso, y no va a explotar en la cara!

+1

¡Gran sugerencia! ¿Cuál es su forma preferida de extraer el elemento resultante de la lista devuelta en las lecturas? Dos '' primeras llamadas? –

+7

Desde base-4.6, hay ['readMaybe :: Read a => String -> Maybe a'] (http://hackage.haskell.org/package/base-4.9.0.0/docs/Text-Read.html #v: readMaybe) en 'Text.Read', que es más conveniente que usar' read' en este caso. – sjakobi

0
filterNumberFromString :: String -> String 
filterNumberFromString s = 
    let allowedString = ['0'..'9'] ++ ['.', ','] 
     toPoint n 
      | n == ',' = '.' 
      | otherwise = n 

     f = filter (`elem` allowedString) s 
     d = map toPoint f 
    in d 


convertStringToFloat :: String -> Float 
convertStringToFloat s = 
    let betterString = filterNumberFromString s 
     asFloat = read betterString :: Float 
    in asFloat 

print (convertStringToFloat "15,00" + 1) 

-> Láminas 16.0

Ése es cómo resolvieron este trabajo en mi proyecto.