gpt4 book ai didi

local - SML 中 "local"和 "let"之间的区别

转载 作者:行者123 更新时间:2023-12-04 03:36:26 26 4
gpt4 key购买 nike

对于 SML 中的“local”和“let”关键字之间的区别,我找不到适合初学者的答案。有人可以提供一个简单的例子并解释什么时候使用一个而不是另一个吗?

最佳答案

(TL;DR)

  • 使用case ... of ...当您只有一个临时绑定(bind)时。
  • 使用let ... in ... end对于非常具体的辅助功能。
  • 切勿使用 local ... in ... end .请改用不透明的模块。

  • sepp2k's fine answer 添加一些关于用例的想法:
  • (总结)local ... in ... end是一个声明和 let ... in ... end是一个表达式,因此有效地限制了它们的使用位置:允许声明的地方(例如,在顶层或模块内部)和内部值声明( valfun ),分别。

    但那又怎样?通常似乎两者都可以使用。 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主要用于当您有一个或多个临时声明(例如辅助函数)在使用后要隐藏,但它们应该在多个非本地声明之间共享。例如。
    (* 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 的一些好处:
  • 每次函数调用都会计算一次绑定(bind)(即使多次使用)。
  • 绑定(bind)可以同时解构(此处为 leftright)。
  • 声明的范围是有限的。 (与 local ... in ... end 的参数相同。)
  • 内部函数可以使用外部函数的参数,也可以使用外部函数本身。
  • 多个相互依赖的绑定(bind)可以整齐地排列。

  • 等等……真的,let 表达式非常好。

    当一个辅助函数被使用一次时,你不妨将它嵌套在 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 ... 的一个缺点:模式 block 不会停止,因此嵌套它们通常需要括号。您还可以以不同的方式将两者结合起来,例如
    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 .由于它的工作是将一组辅助声明隔离到您的主要声明的子集,这迫使您根据它们所依赖的内容对这些主要声明进行分组,而不是按照更理想的顺序进行分组。

    一个更好的选择是简单地编写一个结构,给它一个签名,并使该签名不透明。这样,所有内部声明都可以在整个模块中自由使用而无需导出。

    j4cbo 的 SML on Stilts web-framework 中的一个示例是模块 StaticServer: 它只导出 val server : ... , 即使结构也包含两个声明 structure U = WebUtilval 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
  • 关于local - SML 中 "local"和 "let"之间的区别,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39577498/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com