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
Die Eingabe erfolgt in dem Format ["Apple","15.00","5"]
, das ich mit der Haskell-Funktion words
getrennt habe.
Ich erhalte die folgende Fehlermeldung, die ich denke, weil makeGroceryItem
eine Float
und eine Int
akzeptiert.
*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*
Aber wie mache ich b
und c
vom Typ Float
bzw. Int
?
read
kann einen String in float und int parsen:
Prelude> :set +t
Prelude> read "123.456" :: Float
123.456
it :: Float
Prelude> read "123456" :: Int
123456
it :: Int
Aber das Problem (1) liegt in Ihrem Muster:
createGroceryItem (a:b:c) = ...
Hier ist :
ein (rechtsassoziativer) Binäroperator, der ein Element einer Liste voranstellt. Die RHS eines Elements muss eine Liste sein. Daher wird Haskell mit dem Ausdruck a:b:c
die folgenden Typen ableiten:
a :: String
b :: String
c :: [String]
c
wird als Liste von Strings gedacht. Natürlich kann es nicht read
sein oder an Funktionen übergeben werden, die einen String erwarten.
Stattdessen sollten Sie verwenden
createGroceryItem [a, b, c] = ...
wenn die Liste genau 3 Elemente enthalten muss, oder
createGroceryItem (a:b:c:xs) = ...
wenn ≥3 Artikel akzeptabel sind.
Auch (2) der Ausdruck
makeGroceryItem a read b read c
wird als makeGroceryItem
interpretiert, wobei 5 Argumente verwendet werden, von denen 2 die read
-Funktion sind. Sie müssen Klammern verwenden:
makeGroceryItem a (read b) (read c)
Auch wenn diese Frage bereits beantwortet wurde, empfehle ich dringend, reads
für die Konvertierung von Strings zu verwenden, da sie viel sicherer ist, da sie nicht mit einer nicht behebbaren Ausnahme fehlschlägt.
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)]
[]
Bei Erfolg gibt reads
eine Liste mit genau einem Element zurück: Ein Tuple, das aus dem konvertierten Wert und möglicherweise nicht konvertierbaren zusätzlichen Zeichen besteht. Bei einem Fehler gibt reads
eine leere Liste zurück.
Es ist einfach, bei Erfolg und Misserfolg ein Pattern-Match zu erstellen, und es wird Ihnen nicht ins Gesicht springen!
Zwei Dinge:
createGroceryItem [a, b, c] = makeGroceryItem a (parse b) (parse c)
-- pattern match error if not exactly 3 items in list
oder alternativ
createGroceryItem (a : b : c : _) = makeGroceryItem a (parse b) (parse c)
-- pattern match error if fewer than 3 items in list, ignore excess items
weil :
nicht gleich ++
ist.
In der Zwischenzeit auf der rechten Seite - der Seite, die Ihnen die Fehlermeldung anzeigt - müssen Sie die Ausdrücke mit Klammern gruppieren. Andernfalls wird parse
als Wert interpretiert, den Sie an makeGroceryItem
übergeben möchten. Der Compiler beschwert sich also, wenn Sie versuchen, 5 Argumente an eine Funktion zu übergeben, die nur 3 Parameter benötigt.
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)
-> druckt 16.0
So habe ich diese Aufgabe in meinem Projekt gelöst.