web-dev-qa-db-fra.com

Convertir une chaîne en entier/float dans Haskell?

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?

67
Ranhiru Cooray

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)
84
kennytm

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!

76
LukeN

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.

5
dave4420
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.

0
chrisheyn