我找不到一个初学者友好的答案,解决了SML中"local"和"let"关键字之间的区别.有人可以提供一个简单的例子,并解释何时使用另一个?
case ... of ...时,你只能有一个临时的结合.let ... in ... end非常具体的辅助功能.local ... in ... end.请改用不透明模块.(总结) local ... in ... end是声明和let ... in ... end是一个表达式,以便有效地限制在那里它们可以被用于:当声明被允许(例如在顶层或内部的模块),以及值声明内(val和fun),分别.
但那又怎么样?似乎任何一种都可以使用.的Rosetta石头快速排序代码,例如,可以使用任一构造中,由于辅助函数只使用一次:
(* 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
Run Code Online (Sandbox Code Playgroud)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
Run Code Online (Sandbox Code Playgroud)
如果没有多个,你可以用一个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
Run Code Online (Sandbox Code Playgroud)
以下是一些好处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
Run Code Online (Sandbox Code Playgroud)
这些是等价的.你可能喜欢这种或那种的风格.该case ... of ...有一个好处,虽然是它也为工作和类型('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
Run Code Online (Sandbox Code Playgroud)
缺点是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
Run Code Online (Sandbox Code Playgroud)
不过,这不是关于不使用的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
Run Code Online (Sandbox Code Playgroud)
等等.基本上,
local ... in ... end结束点let ... in ... end是无效的.(local ... in ... end没用.)
你永远不想用local ... in ... end.由于它的工作是将一组辅助声明隔离到主声明的子集,这迫使您根据它们所依赖的内容对这些主声明进行分组,而不是更理想的顺序.
一个更好的选择就是编写一个结构,给它一个签名并使该签名不透明.这样,所有内部声明都可以在整个模块中自由使用而无需导出.
j4cbo在Stilts Web框架上的SML中的一个例子是模块StaticServer:它只导出val server : ...,即使结构也包含两个声明structure U = WebUtil和val content_type = ....
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
Run Code Online (Sandbox Code Playgroud)简短的回答是:local是一个声明,let是一个表达式。因此,它们被用在不同的语法上下文中,并且需要在和local之间进行声明,而需要在其中有一个表达式。没有比这更深的了。inendlet
正如 @SimonShine 提到的,local通常不鼓励使用模块。
| 归档时间: |
|
| 查看次数: |
4071 次 |
| 最近记录: |