我找不到一个初学者友好的答案,解决SML中“local”和“let”关键字之间的区别。有人可以提供一个简单的例子,并解释何时使用另一个?
答案 0 :(得分:6)
case ... of ...
。let ... in ... end
表示非常具体的帮助函数。local ... in ... end
。改为使用不透明模块。 (摘要) local ... in ... end
是一个声明,let ... in ... end
是一个表达式,因此可以有效地限制它们的使用位置:允许声明的位置(例如在顶级或模块内部)和内部值声明(val
和fun
)。
但是那又怎样?似乎任何一种都可以使用。例如,Rosetta Stone QuickSort code可以使用其中任何一个来构造,因为辅助函数只使用一次:
(* First using local ... in ... end *)
local
fun par_helper([], x, l, r) = (l, r)
| par_helper(h::t, x, l, r) =
if h <= x
then par_helper(t, x, l @ [h], r)
else par_helper(t, x, l, r @ [h])
fun par(l, x) = par_helper(l, x, [], [])
in
fun quicksort [] = []
| quicksort (h::t) =
let
val (left, right) = par(t, h)
in
quicksort left @ [h] @ quicksort right
end
end
(* Second using let ... in ... end *)
fun quicksort [] = []
| quicksort (h::t) =
let
fun par_helper([], x, l, r) = (l, r)
| par_helper(h::t, x, l, r) =
if h <= x
then par_helper(t, x, l @ [h], r)
else par_helper(t, x, l, r @ [h])
fun par(l, x) = par_helper(l, x, [], [])
val (left, right) = par(t, h)
in
quicksort left @ [h] @ quicksort right
end
local ... in ... end
主要用于您在使用后要隐藏的一个或多个临时声明(例如辅助函数),但它们应在 multiple之间共享非本地声明。 E.g。
(* Helper function shared across multiple functions *)
local
fun par_helper ... = ...
fun par(l, x) = par_helper(l, x, [], [])
in
fun quicksort [] = []
| quicksort (h::t) = ... par(t, h) ...
fun median ... = ... par(t, h) ...
end
如果没有多个,您可以改为使用let ... in ... end
。
您始终可以避免使用local ... in ... end
来支持 opaque modules (见下文)。
let ... in ... end
主要用于在函数内部计算临时结果或解构产品类型(元组,记录)的值一次或多次时。 E.g。
fun quicksort [] = []
| quicksort (x::xs) =
let
val (left, right) = List.partition (fn y => y < x) xs
in
quicksort left @ [x] @ quicksort right
end
以下是let ... in ... end
的一些好处:
left
和right
)。local ... in ... end
相同的参数。)
等等......真的,让表达式非常好。
当使用辅助函数一次时,您也可以将其嵌套在let ... in ... end
内。
特别是如果使用其他原因也适用。
(case ... of ...
太棒了。)
如果您只有一个let ... in ... end
,则可以改为编写。
fun quicksort [] = []
| quicksort (x::xs) =
case List.partition (fn y => y < x) xs of
(left, right) => quicksort left @ [x] @ quicksort right
这些是等效的。你可能喜欢这种或那种的风格。 case ... of ...
有一个优点,即它也适用于sum types('a option
,'a list
等),例如。
(* Using case ... of ... *)
fun maxList [] = NONE
| maxList (x::xs) =
case maxList xs of
NONE => SOME x
| SOME y => SOME (Int.max (x, y))
(* Using let ... in ... end and a helper function *)
fun maxList [] = NONE
| maxList (x::xs) =
let
val y_opt = maxList xs
in
Option.map (fn y => Int.max (x, y)) y_opt
end
case ... of ...
的一个缺点:模式块不会停止,因此嵌套它们通常需要括号。您也可以用不同的方式将两者结合起来,例如
fun move p1 (GameState old_p) gameMap =
let val p' = addp p1 old_p in
case getMapPos p' gameMap of
Grass => GameState p'
| _ => GameState old_p
end
这与使用local ... in ... end
的 并非如此。
隐藏不在其他地方使用的声明是明智的。 E.g。
(* if they're overly specific *)
fun handvalue hand =
let
fun handvalue' [] = 0
| handvalue' (c::cs) = cardvalue c + handvalue' cs
val hv = handvalue' hand
in
if hv > 21 andalso hasAce hand
then handvalue (removeAce hand) + 1
else hv
end
(* to cover over multiple arguments, e.g. to achieve tail-recursion, *)
(* or because the inner function has dependencies anyways (here: x). *)
fun par(ys, x) =
let fun par_helper([], l, r) = (l, r)
| par_helper(h::t, l, r) =
if h <= x
then par_helper(t, l @ [h], r)
else par_helper(t, l, r @ [h])
in par_helper(ys, [], []) end
等等。基本上,
local ... in ... end
超过let ... in ... end
的点无效。 (local ... in ... end
没用。)
您永远不想使用local ... in ... end
。由于它的工作是将一组辅助声明隔离到主声明的子集,这迫使您根据它们所依赖的内容对这些主声明进行分组,而不是更理想的顺序。
更好的选择是简单地编写一个结构,给它一个签名并使该签名不透明。这样,所有内部声明都可以在整个模块中自由使用而无需导出。
j4cbo的SML on Stilts web-framework中的一个示例是模块StaticServer:它仅导出val server : ...
,即使该结构还包含两个声明structure U = WebUtil
和val content_type = ...
。 / p>
structure StaticServer :> sig
val server: { basepath: string,
expires: LargeInt.int option,
headers: Web.header list } -> Web.app
end = struct
structure U = WebUtil
val content_type = fn
"png" => "image/png"
| "gif" => "image/gif"
| "jpg" => "image/jpeg"
| "css" => "text/css"
| "js" => "text/javascript"
| "html" => "text/html"
| _ => "text/plain"
fun server { basepath, expires, headers } (req: Web.request) = ...
end
答案 1 :(得分:4)
简短的回答是:local
是一个声明,let
是一个表达式。因此,它们用于不同的语法上下文,local
需要in
和end
之间的声明,而let
需要在那里表达。它没有那么深刻。
正如@SimonShine所提到的,local
经常不赞成使用模块。