所以我们有以下数据类型:
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')
Run Code Online (Sandbox Code Playgroud)
和
getvar :: Env -> Char -> Maybe Double
getvar [] c = Nothing
getvar ((x,y):es) c
| c == x = Just y
|otherwise = getvar es c
Run Code Online (Sandbox Code Playgroud)
因此,如果我们运行以下代码:
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。
显然,我的解释存在巨大的缺陷,因为我们尝试“添加”Num 5 和 Just 2,而根据我们的定义,这是不可能的。然而,该功能似乎以某种方式完美地工作,但我就是无法弄清楚。
让我们将其分解为几个步骤。首先,do符号在 Haskell 中并不神奇。它为普通操作员脱糖。那么让我们看看您的最后一个案例evalDo。
evalDo e (Add a b) = do
a' <- evalDo e a
b' <- evalDo e b
Just (a'+b')
Run Code Online (Sandbox Code Playgroud)
根据解构规则,这相当于
evalDo e (Add a b) =
evalDo e a >>= \a' ->
evalDo e b >>= \b' ->
Just (a' + b')
Run Code Online (Sandbox Code Playgroud)
因此,当我们调用 时evalDo [('x', 2)] (Add (Num 5) (Var 'x')),我们首先匹配该Add a b行。a = Num 5我们将使用和来处理这种情况b = Var 'x'。在那个分支里面,我们evalDo都a和b。
但我们不会将结果分配给变量。如果我们使用了let, 那么a'将等于Just 5和b'to Just 2。但事实并非如此。我们使用了<-,它将脱糖转换为一元绑定。我们来看第一个evalDo。看起来像这样。
evalDo e a >>= \a' -> ...
Run Code Online (Sandbox Code Playgroud)
该evalDo部分返回Just 5。运算>>=符(通常发音为“bind”)Maybe定义为
(Just x) >>= k = k x
Nothing >>= _ = Nothing
Run Code Online (Sandbox Code Playgroud)
如果左侧的计算结果为 a Just,则右侧的(这是一个函数)将被调用,并将其内部Just作为参数。如果左侧的计算结果为Nothing,则右侧的(又是一个函数)永远不会被调用。
在我们的例子中,evalDo e a计算结果为Just 5,因此我们使用参数 来调用 lambda 函数5。lambda 调用它的参数a',因此我们有a'equal to 5, 的内部Just。
同样的事情发生在第二种情况下,用Just 2代替Just 5。如果我们给它一个不存在的变量名,那么我们将得到 a Nothing,lambda 永远不会被调用,整个函数将返回Nothing。这个do符号只是让所有这些高阶函数的废话变得透明,所以我们不必考虑太多。
evalDo e (Add a b) = do
a' <- evalDo e a
b' <- evalDo e b
Just (a'+b')
Run Code Online (Sandbox Code Playgroud)
回到这一点,您通常应该阅读的方式是“评估a、评估b和返回a' + b'”。绑定<-带有一些上下文。在您的情况下,该上下文是 failure via Maybe,但原则上它可以是局部变量、非确定性,甚至是延续。这是通过 lambda实现的,但在通常的 Haskell 编程和编写符号过程中do,您不会这样考虑。
| 归档时间: |
|
| 查看次数: |
199 次 |
| 最近记录: |