(* 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 主要用于您在使用后要隐藏的一个或多个临时声明(例如辅助函数),但它们应在多个非本地声明之间共享 . 例如 .
(* 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 来支持不透明模块(参见下文) .
let ... in ... end 主要用于在函数内部计算临时结果或解构产品类型(元组,记录)的值一次或多次时 . 例如 .
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 .
隐藏在其他地方不会使用的声明是明智的 . 例如 .
(* 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 . 由于它的工作是将一组辅助声明隔离到主声明的子集,这迫使您根据它们所依赖的内容对这些主声明进行分组,而不是可能是更理想的顺序 .
2 回答
简短的回答是:
local
是一个声明,let
是一个表达式 . 因此,它们在不同的语法上下文中使用,并且local
需要in
和end
之间的声明,而let
需要在那里使用表达式 . 它没有那么深刻 .正如@SimonShine所提到的,
local
经常被劝阻使用模块 .(TL; DR)
当您只有一个临时绑定时使用
case ... of ...
.使用
let ... in ... end
用于非常特定的辅助函数 .永远不要使用
local ... in ... end
. 请改用不透明模块 .添加一些关于用例的想法到sepp2k的答案:
local ... in ... end
是一个声明,let ... in ... end
是一个表达式,因此有效地限制了它们的使用位置:允许声明的地方(例如在顶层或模块内)和内部值声明(val
和fun
),分别 .但那么呢?似乎任何一种都可以使用 . 例如,Rosetta Stone QuickSort code可以使用其中任何一个来构造,因为辅助函数只使用一次:
因此,让我们关注何时使用其中一个特别有用 .
local ... in ... end
主要用于您在使用后要隐藏的一个或多个临时声明(例如辅助函数),但它们应在多个非本地声明之间共享 . 例如 .如果没有多个,您可以使用
let ... in ... end
代替 .您始终可以避免使用
local ... in ... end
来支持不透明模块(参见下文) .let ... in ... end
主要用于在函数内部计算临时结果或解构产品类型(元组,记录)的值一次或多次时 . 例如 .以下是
let ... in ... end
的一些好处:每个函数调用计算一次绑定(即使多次使用) .
可以同时解构绑定(此处为
left
和right
) .声明的范围有限 . (与
local ... in ... end
相同的参数 . )内部函数可以使用外部函数的参数或外部函数本身 .
可以整齐地排列相互依赖的多个绑定 .
等等......真的,让表达式非常好 .
当使用辅助函数一次时,您也可以将其嵌套在
let ... in ... end
中 .特别是如果使用其他原因也适用 .
一些其他意见
case ... of ...
太棒了 . )如果您只有一个
let ... in ... end
,则可以改为例如这些是等价的 . 你可能喜欢这种或那种的风格 .
case ... of ...
有一个优点,因为它也适用于sum types('a option
,'a list
等),例如case ... of ...
的一个缺点:模式块不会停止,因此嵌套它们通常需要括号 . 你也可以用不同的方式将两者结合起来,例如:但这并不是关于不使用
local ... in ... 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 = ...
.