首页 > 解决方案 > 为什么我使用 `let` 而不仅仅是 `val` 在 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()但我不明白为什么。为什么 val 不能在没有 的函数内使用let

标签: sml

解决方案


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

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

- 表达式的句法结构let是. 因此,在使用 -表达式作为函数体时, in相当于函数体,但具有扩展的本地范围,无论添加什么。letdecinexpendletexpletdec

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

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

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-expressions 主要声明临时值时,您还可以考虑使用case-of. 有关SML 中“local”和“let”之间的区别以及NJ ML 中的嵌套本地声明,请参阅问答。


推荐阅读