在Haskell中将字符串转换为Integer / Float?

时间:2010-03-18 08:43:49

标签: haskell floating-point int

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接受FloatInt

*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

5 个答案:

答案 0 :(得分:84)

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

将被解释为makeGroceryItem,其中包含5个参数,其中2个是read函数。你需要使用括号:

makeGroceryItem a (read b) (read c)

答案 1 :(得分:76)

即使这个问题已经有了答案,我强烈建议使用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会返回一个空列表。

成功与失败的模式匹配很容易,而且它不会在你脸上爆炸!

答案 2 :(得分:5)

两件事:

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的值,因此当您尝试将5个参数传递给只有3个参数的函数时,编译器会抱怨。

答案 3 :(得分:0)

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

这就是我在项目中如何解决这个问题。

答案 4 :(得分:0)

readMaybe 可用于此目的。它也是一个 total 函数,而不是 read(可能引发异常)。

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