在 ML 中用 let 解构元组

destructuring tuple with let in ML

在 sml 中,我可以在 let 中使用元组吗?如果是这样,语法是什么? 我可以要求它作为同心模式匹配,但似乎应该有更少的样板方式。在下面的 let 中,我想将 v1v2 绑定到调用 interpExp 返回的元组的两个值。然后我想用其中一个值调用 interpExp 以获得另外两个值。

fun performOp (e1,op,e2,table) = 
    let val (v1,t1) = interpExp(e1,table)   (* interpExp returns 2-tuple, bind v1 and t2 to those two values *)
        and val (v2,t2) = interpExp(e1,t1)  (* use t1 which was bound on previous line *)
        and val v3 = (case op of
                           Plus => v1 + v2
                     | Minus => v1 - v2
                     | Times => v1 * v2
                     | Div => v1 / v2)
    in (v3,t2)
    end

在进一步的试验和错误中,似乎第二个和第三个 val 是不必要的,此后 v1v2 似乎不在下一个子句的范围内。

fun performOp (e1,op,e2,table) = 
    let val (v1,t1) = interpExp(e1,table) 
        and (v2,t2) = interpExp(e1,t1)  (* oops  t1, not in scope *)
        and v3 = (case op of
                           Plus => v1 + v2
                     | Minus => v1 - v2
                     | Times => v1 * v2
                     | Div => v1 / v2)
    in (v3,t2)
    end

在进一步的实验中,我发现了另一种语法,但我不确定有什么区别,即在 let 中使用 val 而不是 and

fun performOp (e1,op,e2,table) = 
    let val (v1,t1) = interpExp(e1,table) 
        val (v2,t2) = interpExp(e1,t1)  (* oops  t1, not in scope *)
        val v3 = (case op of
                           Plus => v1 + v2
                     | Minus => v1 - v2
                     | Times => v1 * v2
                     | Div => v1 / v2)
    in (v3,t2)
    end

一般来说,and关键字在SML中用于引入多个相互递归的函数或数据类型。例如,我们可以将无偿低效的奇偶校验定义为

fun isEven 0 = true
  | isEven 1 = false
  | isEven n = isOdd (n - 1)

and isOdd 0 = false
  | isOdd 0 = true
  | isOdd n = isEven (n - 1)

以及数据类型的示例:

datatype 'a exposed = Exposed of 'a * 'a stream
and      'a stream  = Stream of unit -> 'a exposed

但是,and 在其他一些上下文中也受支持(我假设是为了保持一致性?),它仅具有同时引入所有绑定的效果。例如,在

val x = 7
and y = 8

同时引入xy。现在直接解决您的直接示例:

  1. 您提出的第一个会遇到语法错误,因为它应该是 and 而不是 val and
  2. 您提供的第二个同时介绍了所有这些绑定,因此您不能依赖第二个中的 t1——它还没有被介绍。
  3. 关于使用 val
  4. 最后一个是正确的

然而,所有这些也存在语法错误,因为 op 也是一个保留字(它主要用于允许中缀函数被视为前缀函数,如 val sum = foldr op+ 0 中)。如果你改变这个,给你留下像

这样的东西
fun performOp (e1, binop, e2, table) = 
    let val (v1, t1) = interpExp (e1, table) 
        val (v2, t2) = interpExp (e1, t1)
        val v3 = (case binop of
                   Plus  => v1 + v2
                 | Minus => v1 - v2
                 | Times => v1 * v2
                 | Div   => v1 / v2)
    in (v3,t2)
    end

那么它应该会按预期工作。我还应该注意到 / 用于 reals 的除法,所以如果你打算用 ints 代替,它应该是 div (也是中缀) .