2010-03-18 20 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 

L'input sarà nel formato ["Apple","15.00","5"] che ho interrotto utilizzando la funzione words di Haskell.Converti stringa in intero/virgola mobile in Haskell?

Ottengo il seguente errore che penso sia perché makeGroceryItem accetta uno Float e uno 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* 

Ma come faccio a fare b e c di tipo Float e Int rispettivamente?

+0

hai un progetto interessante. cosa serve? –

risposta

80

read in grado di analizzare una stringa in float e int:

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

Ma il problema (1) è nel vostro modello:

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

Qui : è un (destra-associativo) operatore binario che antepone un elemento a un elenco. L'RHS di un elemento deve essere una lista. Pertanto, data l'espressione a:b:c, Haskell sarà dedurre i seguenti tipi:

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

cioè c sarà pensato come una lista di stringhe. Ovviamente non può essere read o passato in nessuna funzione in attesa di una stringa.

Invece si dovrebbe usare

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

se la lista deve avere esattamente 3 articoli o

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

se ≥ 3 articoli è accettabile.

anche (2), l'espressione

makeGroceryItem a read b read c 

sarà interpretato come makeGroceryItem prendendo 5 argomenti, di cui 2 funzione read. È necessario utilizzare le parentesi:

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

@KennyTM: 'leggi" 123.456 ":: Float'. Cosa significa questa sintassi? Cosa è '::' qui? 'Legge una funzione? – Nawaz

+0

@Nawaz: Yes 'read' è una funzione. L'espressione 'f :: T' costringe' f' ad avere il tipo 'T'. – kennytm

+0

@KennyTM: Quindi la sintassi 'read" 123.456 ":: Float' è approssimativamente equivalente a' sscanf ("123.456", "% f", &fnum); 'in C, giusto? – Nawaz

5

due cose:

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

o in alternativa

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

perché : non è lo stesso di ++.

Nel frattempo sul lato destro --- il lato che ti dà il messaggio di errore che vedi --- devi raggruppare le espressioni usando parentesi. Altrimenti parse viene interpretato come un valore che si desidera passare a makeGroceryItem, quindi il compilatore si lamenta quando si tenta di passare 5 argomenti a una funzione che richiede solo 3 parametri.

75

Anche se questa domanda ha già una risposta, suggerisco caldamente di utilizzare reads per la conversione delle stringhe, perché è molto più sicuro, in quanto non fallisce con un'eccezione irrecuperabile.

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)] 
[] 

In caso di successo, reads restituisce una lista con un solo elemento: una tupla consistente del valore convertito e personaggi extra forse unconvertable. In caso di fallimento, reads restituisce una lista vuota.

È facile abbinare il modello in caso di successo o fallimento e non si gonfierà in faccia!

+1

Ottimo suggerimento! Qual è il tuo modo preferito di estrarre l'elemento risultante dall'elenco restituito dalle letture? Due chiamate '' fst''? –

+7

Dalla base-4.6, c'è ['readMaybe :: Read a => String -> Forse a'] (http://hackage.haskell.org/package/base-4.9.0.0/docs/Text-Read.html #v: readMaybe) in 'Text.Read', che è più conveniente di usare' reads' in questo 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) 

-> Stampe 16.0

Ecco come ho risolto questo compito nel mio progetto.