haskell 单胞菌任务

k4ymrczo  于 2022-11-14  发布在  其他
关注(0)|答案(2)|浏览(140)

因此,我们有以下数据类型:
data Expr = Num Double | Add Expr Expr | Var Char deriving Show

type Env = [(Char, Double)]
此外,我们有这两个功能:

evalDo :: Env -> Expr -> Maybe Double
evalDo _ (Num a) = return a
evalDo e (Var c) = getvar e c
evalDo e (Add a b) = do
    a' <- evalDo e a
    b' <- evalDo e b
    Just (a'+b')

getvar :: Env -> Char -> Maybe Double
getvar [] c = Nothing
getvar ((x,y):es) c 
    | c == x = Just y
    |otherwise = getvar es c

因此,如果我们运行以下代码:evalDo [('x', 2)] (Add (Num 5) (Var 'x')),我们将收到:Just 7.0。而evalDo [('x', 2)] (Add (Num 5) (Var 'y'))的计算结果为Nothing
根据我对前面代码的理解,我们的函数与evalDo e (Var c) = getvar e c行匹配。从那里它调用getvar e c,结果是Just 2。然后我们返回到最初的第二次调用,即(Add (Num 5) (Var 'x'))
现在Var 'x'被分解成Just 2,所以最后evalDo e (Add a b) = do...函数行匹配,我们得到Just7.0。
显然,我的解释有一个巨大的缺陷,因为我们试图将Num5和Just 2“相加”,而根据我们的定义,这是不可能的。然而,这个函数似乎在某种程度上毫无瑕疵地工作,但我就是想不通。

gywdnpxw

gywdnpxw1#

让我们把它分解成几个步骤。首先,do符号在Haskell中并不神奇,它对普通的运算符来说是一个反符号。让我们看看evalDo中的最后一个例子。

evalDo e (Add a b) = do
    a' <- evalDo e a
    b' <- evalDo e b
    Just (a'+b')

通过the destructuring rules,这相当于

evalDo e (Add a b) =
    evalDo e a >>= \a' ->
    evalDo e b >>= \b' ->
    Just (a' + b')

因此,当我们调用evalDo [('x', 2)] (Add (Num 5) (Var 'x'))时,我们 * 首先 * 匹配Add a b行,我们将用a = Num 5b = Var 'x'来做这个例子,在这个分支中,我们同时使用ab
但是我们 * 不 * 将结果赋给变量。如果我们使用let,那么a'将等于Just 5b'将等于Just 2。但实际情况并非如此。我们使用<-,它将简化为一元绑定。让我们来看看第一个evalDo。它看起来像这样。

evalDo e a >>= \a' -> ...

evalDo部分返回Just 5Maybe上的>>=运算符(通常读作“bind”)定义为

(Just x) >>= k      = k x
Nothing  >>= _      = Nothing

如果左边的值为Just,那么右边的函数将以Justinside 作为参数被调用;如果左边的值为Nothing,那么右边的函数将 *never call *。
在我们的例子中,evalDo e a的计算结果是Just 5,所以我们调用参数为5的lambda函数,lambda函数调用它的参数a',所以我们得到a'等于55Just的内部。
第二种情况也是一样的,用Just 2代替Just 5。如果我们给它一个不存在的变量名,那么我们会得到一个Nothing,lambda永远不会被调用,整个函数会返回Nothingdo符号只是让所有这些高阶函数变得毫无意义。所以我们不用想那么多。

evalDo e (Add a b) = do
    a' <- evalDo e a
    b' <- evalDo e b
    Just (a'+b')

回到这里,通常应该这样理解:“对a求值,对b求值,并返回a' + b'“。<-绑定带有一些上下文。在您的例子中,上下文是通过Maybe失败的,但原则上它可以是一个局部变量,非确定性,这是通过lambda实现的,但是在用Haskell编程和写do表示法的过程中,你不会这样想。

jjjwad0x

jjjwad0x2#

我认为您缺少的部分是<-不仅仅是一个赋值运算符;它会“打开”Just值,并将找到的值绑定到a'b'

evalDo e (Add a b) = 
    evalDo e a >>= \a' -> 
    evalDo e b >>= \b' -> 
    Just (a' + b')

让我们看看Maybe类型的>>=

Nothing >>= _ = Nothing
Just x >>= f = f x -- Call f on x, not on Just x

如果evalDo e a产生Nothing,我们可以忽略do块的其余部分,直接计算Nothing,但如果它是Just 5,我们将5(而不是Just 5)绑定到a'并继续,同样的事情也发生在evalDo e b:要么我们得到Just 2,将2绑定到b',然后继续,要么立即返回Nothing
只有当 * 两个 * 递归调用都产生Just值时,我们才能到达最后一行,在这里我们可以添加5 + 2并将结果 Package 在Just中。

相关问题