Haskell自定义Parser中<*、*>操作符工作原理与字符串传递逻辑疑问
剩余字符串传递逻辑解析
你观察到的字符串传递逻辑,完全由你已经实现的Applicative的<*>方法定义,*>和<*>都是Applicative类型类提供的默认派生方法,没有额外的隐藏逻辑。
前置说明:parseChar的参考实现
为了方便走流程,我们先明确示例中parseChar的常见实现:
parseChar :: Char -> Parser Char parseChar c = Parser $ \s -> case s of (x:xs) | x == c -> Just (c, xs) _ -> Nothing
第一步:明确*>和<*的默认实现
这两个操作符不需要你手动实现,是基于你已经写好的fmap和<*>派生的:
-- 丢弃左边解析值,保留右边解析值 (*>) :: Applicative f => f a -> f b -> f b p1 *> p2 = fmap (\_ b -> b) p1 <*> p2 -- 保留左边解析值,丢弃右边解析值 (<*) :: Applicative f => f a -> f b -> f a p1 <* p2 = fmap (\a _ -> a) p1 <*> p2
第二步:结合你实现的<*>逻辑看字符串传递
你自己写的<*>实例逻辑非常明确:
- 先拿输入字符串运行左侧的解析器,得到解析结果(函数值)和剩余字符串str1
- 直接把
str1作为输入运行右侧的解析器,得到解析结果(参数值)和剩余字符串str2 - 把函数应用到参数上,最终返回(应用后的结果, str2)
不管是*>还是<*,都只是修改了<*>最终返回的结果值,完全不会改变「从左到右执行、前一个解析器的剩余输出是后一个解析器的输入」的执行流程。
第三步:走一遍你的示例完整流程
你的示例runParser (parseChar 'x' *> parseChar 'a' <* parseChar 'x') "xax"的执行顺序是左结合,等价于runParser ((parseChar 'x' *> parseChar 'a') <* parseChar 'x') "xax":
- 先执行
parseChar 'x' *> parseChar 'a':- 输入字符串是
"xax",先跑parseChar 'x'匹配到x,返回剩余字符串"ax",同时按照*>的定义把解析值'x'替换为函数\_ b -> b - 把
"ax"传给parseChar 'a'执行,匹配到a,返回剩余字符串"x" - 函数应用得到结果
'a',这一步执行完成后得到的中间结果是('a', "x")
- 输入字符串是
- 再执行
<* parseChar 'x':- 把上一步得到的剩余字符串
"x"作为输入传给parseChar 'x'执行,匹配到x,返回剩余字符串"" - 按照
<*的定义保留左边的'a'作为最终结果
- 把上一步得到的剩余字符串
- 最终返回
Just ('a', ""),和你的示例结果一致。
核心总结
- 解析器永远从左到右顺序执行,剩余字符串按执行顺序依次向后传递,这个逻辑是你写的
<*>实例已经定义好的 - 箭头指向仅用于决定最终保留哪一侧的解析值,和字符串传递、执行顺序没有任何关系
内容的提问来源于stack exchange,提问作者apocalyptica1972
相关产品推荐
相关产品推荐

