好吧,我正在尝试学习 Haskell。
这是我的解析器。
import Data.Char
data Parser a = MkParser (String -> Maybe (String, a))
这是一个解析器,它解析一个字符串一次,然后根据它解析的内容,运行另一个解析器。
-- second parse depends on the first
(<<<=) :: Parser a -> (a -> Parser b) -> Parser b
-- a parse, gives Nothing | Just (string, ans depending on a)
(MkParser pa) <<<= k = MkParser sf
where
sf inp = case pa inp of
Nothing -> Nothing
Just (cs, c) -> unParser (k c) cs
unParser :: Parser a -> String -> Maybe (String, a)
-- applies a parser manually
unParser (MkParser pa) inp = pa inp
现在我在这里使用它(
isDigit
只是检查字符是否是数字,isAlpha
检查它是否是字母)。我还使用这个基本解析器(解析字符是否是我正在寻找的)
char :: Char -> Parser Char
char wanted = MkParser sf
where
sf (c:cs) | c == wanted = Just (cs, c)
sf _ = Nothing
现在当我这样做时:
t = satisfy isAlpha <<<= \x -> satisfy isDigit <<<= \y -> char x
有效!
但是当我把它放在括号里时
t = satisfy isAlpha <<<= (\x -> satisfy isDigit) <<<= \y -> char x
它给出一个错误,说变量
x
不在范围内......为什么?
我最初只是放置了括号,因为这样更容易阅读。但事实证明我的代码只有在删除括号时才有效。
我尝试使用它并意识到这也有效:
t = satisfy isAlpha <<<= (\x -> satisfy isDigit <<<= \y -> char x)
问题1)但是为什么呢?如果它先执行
(\x -> satisfy isDigit <<<= \y -> char x)
,它就不知道 x
是什么,那么它是如何工作的呢?
问题2)如果不使用括号,评估顺序是什么?
问题 3)为什么我这样做时不起作用
(\x -> satisfy isDigit)
?
所有问题都是相关的,我确信我只是误解了评估顺序。我曾经用 OOP 语言编程,我觉得我误解了 Haskell 中的括号和求值顺序?
在你的第一个例子中:
t = satisfy isAlpha <<<= (\x -> satisfy isDigit) <<<= \y -> char x
看起来您正在将
(<<<=)
运算符的左侧和右侧都视为解析操作。
但实际上,左侧是一个解析动作,而右侧是一个函数,它获取前一个解析器的结果,并返回另一个解析动作。
t = parser1 <<<= parser2 <<<= parser3
where
parser1 = satisfy isAlpha
parser2 x = satisfy isDigit
parser3 y = char x -- x isn’t in scope
在你的第二个例子中:
t = satisfy isAlpha <<<= (\x -> satisfy isDigit <<<= \y -> char x)
这解决了范围问题,因为它澄清了第三个解析器需要在
x
的范围内,换句话说,它需要在其闭包中包含 x
。
t = parser1 <<<= function1
where
parser1 = satisfy isAlpha
function1 x = parser2 <<<= function3
where
parser2 = satisfy isDigit
function3 y = char x