为什么我使用`let`而不仅仅是`val`在SML中的函数内声明一个变量?

时间:2018-05-03 22:43:37

标签: sml

在SML中,我被教导了一种惯用的方法来定义函数的局部变量:

fun correct_fun() =
    let val x = 1
    in x + 2
    end

为什么我必须使用let,而不仅仅是val

fun incorrect_fun() =
    val x = 1
    x + 2

incorrect_fun()抛出错误,但我不明白为什么。为什么不能在没有let的函数内使用val?

1 个答案:

答案 0 :(得分:2)

  

为什么val不能在没有let的函数内使用?

因为val是一种声明,let是一种表达式,函数体是表达式。

let - 表达式的句法结构为let dec in exp end。因此,在使用let - 表达式作为函数体时,exp中的 let 等同于函数体,但具有扩展的局部范围 dec 添加的内容。

let - 表达式允许您使用任何类型的声明,而不仅仅是val声明。

例如,您可以将异常处理用作用于回溯的控制流机制,并且您可以嵌套仅在本地使用的辅助函数,并且可能需要多个参数来存储临时结果,但您可能不希望公开异常或辅助函数。因此对于Eight Queens puzzle,您可以改进this solution(来自关于函数式编程的补充说明,第140-143页,来自Niels Andersen):

fun concatMap f xs = String.concat (List.map f xs)
fun concatTab f n = String.concat (List.tabulate (n, f))

fun dots n = concatTab (fn _ => ". ") n
fun show ys = concatMap (fn y => dots (y - 1) ^ "* " ^ dots (8 - y) ^ "\n") ys

fun queen dims =
    let exception Queen
        fun beats ((x,y),(x1,y1)) = (* x = x1 *)
                         (* orelse *)  y = y1
                            orelse x + y = x1 + y1
                            orelse x - y = x1 - y1

        fun safe ((x, y), _, []) = true
          | safe ((x, y), x1, y1::ys) =
            not (beats ((x, y), (x1, y1))) andalso safe ((x, y), x1 + 1, ys)

        fun queen' ((0, _), ys) = ys
          | queen' ((_, 0), _) = raise Queen
          | queen' ((x, y), ys)  =
            if safe ((x, y), x + 1, ys)
              then queen' ((x - 1, 8), y :: ys)
                   handle Queen => queen' ((x, y - 1), ys)
              else queen' ((x, y - 1), ys)

    in queen' (dims, []) end

证明它;

- print (show (queen ((8,8))));
. . . . * . . . 
. . . . . . * . 
. * . . . . . . 
. . . . . * . . 
. . * . . . . . 
* . . . . . . . 
. . . * . . . . 
. . . . . . . * 

当您使用let - 表达式主要声明临时值时,您还可以考虑使用case-of。有关Difference between "local" and "let" in SMLnested local declarations in ML of NJ的信息,请参阅问答。