4

作为这里第四个练习的一部分, 我想使用一个reads类型函数,比如readHex一个 parsec Parser

为此,我编写了一个函数:

liftReadsToParse :: Parser String -> (String -> [(a, String)]) -> Parser a
liftReadsToParse p f = p >>= \s -> if null (f s) then fail "No parse" else (return . fst . head ) (f s)

可以使用,例如在 GHCI 中,如下所示:

*Main Numeric> parse (liftReadsToParse (many1 hexDigit) readHex) "" "a1"
Right 161

任何人都可以建议在以下方面对这种方法进行任何改进:

  • 在返回的情况下,该术语(f s)是否会被记忆或评估两次? null (f s)False
  • 处理多个成功的解析,即当length (f s)大于一时,我不知道 parsec 如何处理这个问题。
  • 处理解析的其余部分,即(snd . head) (f s).

  • 4

    2 回答 2

    3

    这是个好主意。一种更自然的方法可以使您的ReadS解析器更好地适应 Parsec 是Parser String在类型的开头省略:

    liftReadS :: ReadS a -> String -> Parser a
    liftReadS reader = maybe (unexpected "no parse") (return . fst) .
                       listToMaybe . filter (null . snd) . reader
    

    这种“组合器”风格非常符合 Haskell 的习惯——一旦你习惯了它,它会让函数定义更容易阅读和理解。

    然后,您将liftReadS在简单的情况下像这样使用:

    > parse (many1 hexDigit >>= liftReadS readHex) "" "a1"
    

    (请注意,listToMaybeData.Maybe模块中。)

    在更复杂的情况下,liftReadS很容易在任何 Parsecdo块内使用。

    关于您的其他一些问题:

    1. 该功能reader现在只应用一次,所以没有什么要“记忆”的。
    2. 在大多数情况下,忽略除ReadS解析器中的第一个解析之外的所有内容是常见且公认的做法,所以你很好。
    于 2010-08-25T23:15:56.400 回答
    0

    要回答您问题的第一部分,(f s)不会记住 no,您必须手动执行此操作:

    liftReadsToParse p f = p >>= \s -> let fs = f s in if null fs then fail "No parse"
                                                                  else (return . fst . head ) fs
    

    但我会改用模式匹配:

    liftReadsToParse p f = p >>= \s -> case f s of
                                            []              -> fail "No parse"
                                            (answer, _) : _ -> return answer
    
    于 2010-08-25T19:03:42.390 回答