作为第4次练习的一部分here
我想使用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)
。
答案 0 :(得分: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"
(请注意,listToMaybe
位于Data.Maybe
模块中。)
在更复杂的情况下,liftReadS
很容易在任何内容中使用
Parsec do
阻止。
关于你的其他一些问题:
reader
现在只应用一次,因此没有任何“memoize”。ReadS
解析器中的第一个解析之外的所有解析是常见且被接受的做法,所以你没事。答案 1 :(得分:0)
要回答问题的第一部分,不会记住(f s)
,您必须手动执行此操作:
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