3

我对 Haskell 很陌生,目前正在尝试解决需要一些字符串解析的问题。我的输入字符串包含逗号分隔的引号中的单词列表。我想将此单个字符串解析为字符串的单词列表。我应该从哪里开始学习解析这样的字符串?是否有部分模块和/或功能会有所帮助?

ps 请不要发布完整的解决方案。我只是要求一个指向起点的指针,这样我就可以学习如何去做。

4

6 回答 6

6

我最终决定推出自己的解析功能,因为这是一个如此简单的情况。自从我第一次发布这个问题并想在这里记录我的解决方案以来,我已经学到了很多关于 Haskell 的知识:

split :: Char -> String -> [String]
split _ "" = []
split c s = firstWord : (split c rest)
    where firstWord = takeWhile (/=c) s
          rest = drop (length firstWord + 1) s

removeChar :: Char -> String -> String
removeChar _ [] = []
removeChar ch (c:cs)
    | c == ch   = removeChar ch cs
    | otherwise = c:(removeChar ch cs)

main = do
    handle <- openFile "input/names.txt" ReadMode
    contents <- hGetContents handle
    let names = sort (map (removeChar '"') (split ',' contents))
    print names
    hClose handle
于 2012-07-31T18:28:20.473 回答
6

最强大的解决方案是解析器组合器。Haskell 有其中几个,但我想到的最重要的是:

  • parsec : 一个非常好的通用解析库
  • attoparsec:更快版本的 parsec,它牺牲了错误消息的质量和一些其他功能以获得额外的速度
  • uu-parsinglib : 一个非常强大的解析库

do解析器组合器的一大优点是使用符号(或Applicative样式,如果您愿意)很容易定义解析器。

如果您只想要一些快速简单的字符串操作功能,请查阅text库(对于高性能字节编码字符串)或Data.List(对于普通列表编码字符串),它提供了操作字符串的必要功能。

于 2012-06-26T15:56:25.383 回答
5

由于Strings 只是CharHaskell 中的 s 列表,因此 Data.List将是开始查找的好地方(为了学习 Haskell)。

对于更复杂的情况(例如,逗号可能嵌套在引号内并且应该被忽略),parsec(如 Daniel 提到的)将是一个更好的解决方案。

此外,如果您要解析 CSV,您可以尝试Text.CSV,尽管我没有尝试过,所以我不能说它会有多大帮助。

于 2012-06-26T03:21:09.033 回答
3

这是一种特别厚颜无耻的方法:

parseCommaSepQuotedWords :: String -> [String]
parseCommaSepQuotedWords s = read ("[" ++ s ++ "]")

这可能有效,但它非常脆弱而且相当愚蠢。本质上,您使用的是 Haskell 编写字符串列表的方式几乎与您的方式一致的事实,因此内置Read实例几乎就是您想要的东西。您可以使用reads更好的错误报告,但实际上您可能想要完全做其他事情。

总的来说,parsec真的值得一看——它使用起来很有趣,也是最初让我对 Haskell 感到兴奋的事情之一。但是如果你想要一个本土的解决方案,我经常使用关于 and 的结果的语句来编写简单的case东西。假设您正在寻找输入中的下一个分号。然后将返回,其中:spanbreakbreak (== ';') inp(before, after)

  • beforeinp最多(不包括)第一个分号的内容(如果没有,则为全部)
  • after是字符串的其余部分:
    • 如果after不为空,则第一个元素是分号
    • 不管发生什么,before ++ after == inp

因此,要解析以分号分隔的语句列表,我可能会这样做:

parseStmts :: String -> Maybe [Stmt]
parseStmts inp = case break (== ';') inp of
  (before, _ : after) -> -- ...
    -- ^ before is the first statement
    --     ^ ignore the semicolon
    --           ^ after is the rest of the string
  (_, []) -> -- inp doesn't contain any semicolons
于 2012-06-26T13:30:18.157 回答
1

为了给遇到这个问题的人一个完整的答案,Data.Text也有一些很好的功能。

于 2012-06-26T05:53:58.130 回答
1

将 parsec 用于任何“真正的工作”。

有关介绍,请阅读https://therning.org/magnus/archives/tag/parsec

于 2012-06-26T07:41:51.940 回答