将字符串转换为Haskell中的整数/浮点数?


75
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"]我使用Haskellwords函数分解的格式。

我收到以下错误,我认为是因为makeGroceryItem接受aFloat和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*

但是,如何分别制作bc的类型FloatInt呢?


1
你有一个有趣的项目。这是为了什么?
马丁·菲舍尔

Answers:


92

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

将被解释为带有makeGroceryItem5个参数,其中2个是read函数。您需要使用括号:

makeGroceryItem a (read b) (read c)

@KennyTM: read "123.456" :: Float。这个语法是什么意思?这是::什么 是read功能吗?
Nawaz 2012年

@Nawaz:是read的。的f :: T表达力f有型T
kennytm 2012年

@KennyTM:所以语法read "123.456" ::Float大致相当于sscanf("123.456", "%f", &fnum);C,对吧?
Nawaz 2012年

@Nawaz:仅用于该read功能。有关详细信息,请参见stackoverflow.com/questions/5926826/…
kennytm 2012年

在内部,Read如何实际工作?是否将字符串分解为Char列表。然后,它是否具有从char到int的字典,并将char的列表与字典进行比较以获取相应的int?
CMCDragonkai 2014年

77

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

在成功与失败之间进行模式匹配很容易,并且不会在您的脸上炸毁!


1
很棒的建议!从读取返回的列表中提取结果项的首选方式是什么?有两个fst电话吗
亚历克斯(Alex)2015年

10
从base-4.6开始,存在readMaybe :: Read a => String -> Maybe ain Text.Read,这比reads在这种情况下使用更方便。
sjakobi,2013年

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个参数的函数时,编译器会抱怨。


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

这就是我在项目中解决此任务的方式。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.