在recent question中,我询问了以下内容 parsec解析器:
manyLength
:: forall s u m a. ParsecT s u m a -> ParsecT s u m Int
manyLength p = go 0
where
go :: Int -> ParsecT s u m Int
go !i = (p *> go (i + 1)) <|> pure i
此功能类似于many
。但是,它不是返回[a]
返回能够成功运行p
的次数。
除了一个问题外,这很有效。它不会在常量堆中运行 空间。
在链接问题中,Li-yao
Xia提供了另一种方式
编写使用常量堆空间的manyLength
:
manyLengthConstantHeap
:: forall s u m a. ParsecT s u m a -> ParsecT s u m Int
manyLengthConstantHeap p = go 0
where
go :: Int -> ParsecT s u m Int
go !i =
((p *> pure True) <|> pure False) >>=
\success -> if success then go (i+1) else pure i
这是一项重大改进,但我不明白为什么
manyLengthConstantHeap
使用常量堆空间,而原始manyLength
则不使用。
如果你在(<|>)
中内联manyLength
,它看起来有点像这样:
manyLengthInline
:: forall s u m a. Monad m => ParsecT s u m a -> ParsecT s u m Int
manyLengthInline p = go 0
where
go :: Int -> ParsecT s u m Int
go !i =
ParsecT $ \s cok cerr eok eerr ->
let meerr :: ParserError -> m b
meerr err =
let neok :: Int -> State s u -> ParserError -> m b
neok y s' err' = eok y s' (mergeError err err')
neerr :: ParserError -> m b
neerr err' = eerr $ mergeError err err'
in unParser (pure i) s cok cerr neok neerr
in unParser (p *> go (i + 1)) s cok cerr eok meerr
如果你在(>>=)
中内联manyLengthConstantHeap
,它看起来有点像这样:
manyLengthConstantHeapInline
:: forall s u m a. Monad m => ParsecT s u m a -> ParsecT s u m Int
manyLengthConstantHeapInline p = go 0
where
go :: Int -> ParsecT s u m Int
go !i =
ParsecT $ \s cok cerr eok eerr ->
let mcok :: Bool -> State s u -> ParserError -> m b
mcok success s' err =
let peok :: Int -> State s u -> ParserError -> m b
peok int s'' err' = cok int s'' (mergeError err err')
peerr :: ParserError -> m b
peerr err' = cerr (mergeError err err')
in unParser
(if success then go (i + 1) else pure i)
s'
cok
cerr
peok
peerr
meok :: Bool -> State s u -> ParserError -> m b
meok success s' err =
let peok :: Int -> State s u -> ParserError -> m b
peok int s'' err' = eok int s'' (mergeError err err')
peerr :: ParserError -> m b
peerr err' = eerr (mergeError err err')
in unParser
(if success then go (i + 1) else pure i)
s'
cok
pcerr
peok
peerr
in unParser ((p *> pure True) <|> pure False) s mcok cerr meok eerr
这是完整性的ParsecT构造函数:
newtype ParsecT s u m a = ParsecT
{ unParser
:: forall b .
State s u
-> (a -> State s u -> ParseError -> m b) -- consumed ok
-> (ParseError -> m b) -- consumed err
-> (a -> State s u -> ParseError -> m b) -- empty ok
-> (ParseError -> m b) -- empty err
-> m b
}
为什么manyLengthConstantHeap
以恒定的堆空间运行,而
manyLength
不是吗?它看起来不像go
的递归调用
manyLengthConstantHeap
或manyLength
的尾部调用位置。
将来编写parsec解析器时,如何知道空间
给定解析器的要求?夏丽瑶是怎么知道的
manyLengthConstantHeap
可以吗?
我觉得我没有信心预测哪些解析器会使用 大输入上的大量内存。
是否有一种简单的方法可以确定给定的函数是否存在 Haskell中的尾递归而不运行它?或者更好,没有编译 它?