Convert String to Integer/Float in 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

The input will be in the format ["Apple","15.00","5"] which I broke up using Haskell's words function.

I get the following error which I think is because makeGroceryItem accepts a Float and an 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*

But how do I make b and c of type Float and Int, respectively?

108084 次浏览

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,也不能传递给任何需要字符串的函数。

相反,你应该使用

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)

两件事:

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个参数时,编译器会报警。

尽管这个问题已经有了答案,我还是强烈建议使用 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返回一个只包含一个元素的列表: 一个由转换后的值和可能无法转换的额外字符组成的元组。失败时,reads返回一个空列表。

成功与失败之间很容易进行模式匹配,而且它不会在你面前爆炸!

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

这就是我如何在我的项目中解决这个任务。

readMaybe可以用于此。它也是一个 总数函数,而不是 read函数(它可能抛出异常)。

Prelude> import Text.Read
Prelude Text.Read> readMaybe ("1.5") :: Maybe Float
Just 1.5