web-dev-qa-db-ja.com

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

入力は["Apple","15.00","5"]の形式になり、Haskellのwords関数を使用して分割しました。

makeGroceryItemFloatIntを受け入れるためだと思う次のエラーが表示されます。

*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*

しかし、どのようにしてbc型のFloatIntを作るのでしょうか?

68

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

この質問にはすでに答えがありますが、文字列変換に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は空のリストを返します。

成功と失敗のパターンマッチングは簡単で、あなたの顔に爆発することはありません!

76
LukeN

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

:++と同じではないためです。

一方、右側に表示されるエラーメッセージが表示される側では、かっこを使用して式をグループ化する必要があります。それ以外の場合、parsemakeGroceryItemに渡したい値として解釈されるため、3つのパラメーターしか受け取らない関数に5つの引数を渡そうとするとコンパイラーは文句を言います。

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)

-> 16.0を印刷

それが私のプロジェクトでこのタスクを解決した方法です。

0
chrisheyn