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'entrée sera au format ["Apple","15.00","5"]
que j'ai séparé en utilisant la fonction words
de Haskell.
J'obtiens l'erreur suivante qui, je pense, est due au fait que makeGroceryItem
accepte une Float
et une 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*
Mais comment puis-je créer b
et c
de type Float
et Int
, respectivement?
read
peut analyser une chaîne en float et int:
Prelude> :set +t
Prelude> read "123.456" :: Float
123.456
it :: Float
Prelude> read "123456" :: Int
123456
it :: Int
Mais le problème (1) est dans votre modèle:
createGroceryItem (a:b:c) = ...
Ici, :
est un opérateur binaire (associatif à droite) qui ajoute un élément à une liste. Le RHS d'un élément doit être une liste. Par conséquent, étant donné l'expression a:b:c
, Haskell déduira les types suivants:
a :: String
b :: String
c :: [String]
c
sera considéré comme une liste de chaînes. De toute évidence, il ne peut pas être read
ni passé dans aucune fonction attendant une chaîne.
Au lieu de cela, vous devriez utiliser
createGroceryItem [a, b, c] = ...
si la liste doit comporter exactement 3 éléments, ou
createGroceryItem (a:b:c:xs) = ...
si ≥3 articles est acceptable.
Aussi (2), l'expression
makeGroceryItem a read b read c
sera interprété comme makeGroceryItem
prenant 5 arguments, dont 2 sont la fonction read
. Vous devez utiliser des parenthèses:
makeGroceryItem a (read b) (read c)
Même si cette question a déjà une réponse, je suggère fortement d'utiliser reads
pour la conversion de chaîne, car c'est beaucoup plus sûr, car elle n'échoue pas avec une exception irrécupérable.
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 cas de succès, reads
renvoie une liste avec exactement un élément: un tuple composé de la valeur convertie et éventuellement de caractères supplémentaires non convertibles. En cas d'échec, reads
renvoie une liste vide.
Il est facile de faire un patronage en cas de succès ou d’échec, et il n’explosera pas votre visage!
Deux choses:
createGroceryItem [a, b, c] = makeGroceryItem a (parse b) (parse c)
-- pattern match error if not exactly 3 items in list
ou bien
createGroceryItem (a : b : c : _) = makeGroceryItem a (parse b) (parse c)
-- pattern match error if fewer than 3 items in list, ignore excess items
parce que :
est différent de ++
.
Pendant ce temps, sur le côté droit - le côté qui vous donne le message d'erreur que vous voyez - vous devez regrouper les expressions à l'aide de crochets. Sinon, parse
est interprété comme une valeur que vous souhaitez transmettre à makeGroceryItem
. Le compilateur se plaint donc lorsque vous essayez de transmettre 5 arguments à une fonction ne prenant que 3 paramètres.
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)
-> impressions 16.0
Thats comment j'ai résolu cette tâche dans mon projet.