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
入力は["Apple","15.00","5"]
の形式になり、Haskellのwords
関数を使用して分割しました。
makeGroceryItem
がFloat
と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*
しかし、どのようにしてb
とc
型のFloat
とInt
を作るのでしょうか?
read
は、文字列をfloatとintに解析できます。
Prelude> :set +t
Prelude> read "123.456" :: Float
123.456
it :: Float
Prelude> read "123456" :: Int
123456
it :: Int
しかし、問題(1)はあなたのパターンにあります:
createGroceryItem (a:b:c) = ...
ここに :
は、要素をリストの先頭に追加する(右結合)二項演算子です。要素のRHSはリストでなければなりません。したがって、式a:b:c
、Haskellは次のタイプを推測します。
a :: String
b :: String
c :: [String]
つまり、c
は文字列のリストと見なされます。明らかにread
にしたり、Stringを期待する関数に渡すことはできません。
代わりに使用する必要があります
createGroceryItem [a, b, c] = ...
リストに正確に3つの項目が必要な場合、または
createGroceryItem (a:b:c:xs) = ...
≥3アイテムが許容される場合。
また、(2)、式
makeGroceryItem a read b read c
5つの引数を取るmakeGroceryItem
として解釈され、そのうち2つはread
関数です。括弧を使用する必要があります。
makeGroceryItem a (read b) (read c)
この質問にはすでに答えがありますが、文字列変換にreads
を使用することを強くお勧めします。これは、回復不能な例外で失敗しないため、はるかに安全だからです。
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)]
[]
成功すると、reads
は要素を1つだけ含むリストを返します。変換された値と変換できない余分な文字で構成されるタプルです。失敗すると、reads
は空のリストを返します。
成功と失敗のパターンマッチングは簡単で、あなたの顔に爆発することはありません!
2つのこと:
createGroceryItem [a, b, c] = makeGroceryItem a (parse b) (parse c)
-- pattern match error if not exactly 3 items in list
または代わりに
createGroceryItem (a : b : c : _) = makeGroceryItem a (parse b) (parse c)
-- pattern match error if fewer than 3 items in list, ignore excess items
:
は++
と同じではないためです。
一方、右側に表示されるエラーメッセージが表示される側では、かっこを使用して式をグループ化する必要があります。それ以外の場合、parse
はmakeGroceryItem
に渡したい値として解釈されるため、3つのパラメーターしか受け取らない関数に5つの引数を渡そうとするとコンパイラーは文句を言います。
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)
-> 16.0を印刷
それが私のプロジェクトでこのタスクを解決した方法です。