haskell [[Char]]至[[Int]]

时间:2019-05-13 16:14:23

标签: list haskell char int

我有一个Haskell程序问题。 我正在尝试将[[Char]]更改为[[Int]] 我有

["2","2","1","2,2","1"] 

字符列表 我正在尝试将其更改为[[Int]]

[[2],[2],[1],[2,2],[1]]

我尝试过

f :: [String] -> [Int]
f = map read

但这给了我

  

[2,2,1,***例外:Prelude.read:无解析

有人可以帮我吗?

1 个答案:

答案 0 :(得分:5)

此操作失败的原因是因为字符串"2,2"本身不能转换为Int:这是一个数字,后跟一个逗号,后跟一个数字。 Int由一个可选的负号,后跟一些数字和一些额外的可能性(如十六进制数字)解析,但是让我们暂时忽略它们。

基于预期的输出,您为f指定的类型签名不正确。您的输出类型似乎是Int lists 的列表,所以[[Int]]。这意味着您应将f指定为:

f :: [String] -> [[Int]]
f = ...

因此,我们需要将每个String读到[Int]。我们不能在这里直接使用read,因为read指向[Int]期望字符串以方括号开始和结束。但是,我们可以手动添加这些内容,例如:

f :: [String] -> [[Int]]
f = map (\s -> read ('[' : s ++ "]"))

无积分版本:

f :: [String] -> [[Int]]
f = map (read . ('[' :) . (++ "]"))

例如:

Prelude> f ["2","2","1","2,2","1"] 
[[2],[2],[1],[2,2],[1]]

使用readMaybe

解析更安全

像这样通过String进行解析当然不是很“ 安全”,因为String可能不遵循格式。我们可以使其更加安全,例如使用readMaybe :: Read a => String -> Maybe a

import Text.Read(readMaybe)

f :: [String] -> [Maybe [Int]]
f = map (readMaybe . ('[' :) . (++ "]"))

例如:

Prelude Text.Read> f ["2", "3;2", "4,7,3", "bla"]
[Just [2],Nothing,Just [4,7,3],Nothing]

例如,我们可以使用catMaybes :: [Maybe a] -> [a]来省略失败的读取:

import Data.Maybe(catMaybes)
import Text.Read(readMaybe)

f :: [String] -> [[Int]]
f = catMaybes . map (readMaybe . ('[' :) . (++ "]"))

例如:

Prelude Data.Maybe Text.Read> f ["2", "3;2", "4,7,3", "bla"]
[[2],[4,7,3]]

或如@dfeuer所说,如果 all 解析成功,我们可以使用traverse :: (Applicative f, Traversable t) => (a -> f b) -> t a -> f (t b)返回包裹在[[Int]]中的Just结果,并且Nothing否则:

import Text.Read(readMaybe)

f :: [String] -> Maybe [[Int]]
f = traverse (readMaybe . ('[' :) . (++ "]"))

例如:

Prelude Text.Read> f ["2","2","1","2,2","1"] 
Just [[2],[2],[1],[2,2],[1]]
Prelude Text.Read> f ["2", "3;2", "4,7,3", "bla"]
Nothing

使用readEither

解析错误消息

通过使用readEither :: Read a => String -> Either String a,如果解析失败,我们可以获得包裹在Left中的错误消息:

import Text.Read(readEither)

f :: [String] -> [Either String [Int]]
f = map (readEither . ('[' :) . (++ "]"))

例如:

Prelude Text.Read> f ["2", "3;2", "4,7,3", "bla"]
[Right [2],Left "Prelude.read: no parse",Right [4,7,3],Left "Prelude.read: no parse"]

并以相同的方式使用traverse来获取包裹在Left中的错误消息或完整的结果存储在Right中:

import Text.Read(readEither)

f :: [String] -> Either String [[Int]]
f = traverse (readEither . ('[' :) . (++ "]"))

例如:

Prelude Text.Read> f ["2","2","1","2,2","1"] 
Right [[2],[2],[1],[2,2],[1]]
Prelude Text.Read> f ["2", "3;2", "4,7,3", "bla"]
Left "Prelude.read: no parse"

在这里,就像@dfeuer所说的那样,它实际上并没有显示太多信息。但是,有一些解析器可以提供更多的信息解析错误。