我有一个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:无解析
有人可以帮我吗?
答案 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所说的那样,它实际上并没有显示太多信息。但是,有一些解析器可以提供更多的信息解析错误。