“本地"与“本地"之间的区别是:和“让"在SML中 [英] Difference between "local" and "let" in SML

查看:79
本文介绍了“本地"与“本地"之间的区别是:和“让"在SML中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I couldn't find a beginner friendly answer to what the difference between the "local" and "let" keywords in SML is. Could someone provide a simple example please and explain when one is used over the other?

推荐答案

(TL; DR)

  1. 只有一个临时绑定时使用case ... of ....
  2. let ... in ... end用于非常特定的帮助程序功能.
  3. 请勿使用local ... in ... end.请改用不透明的模块.
  1. Use case ... of ... when you only have one temporary binding.
  2. Use let ... in ... end for very specific helper functions.
  3. Never use local ... in ... end. Use opaque modules instead.

sepp2k的很好的答案中添加了一些用例的想法:

  • (摘要) local ... in ... end是一个声明,而let ... in ... end是一个表达式,因此有效地限制了它们的使用位置:允许在何处声明(例如,在顶层)或在模块内部)和内部值声明(valfun).

    Adding some thoughts on use-cases to sepp2k's fine answer:

    • (Summary) local ... in ... end is a declaration and let ... in ... end is an expression, so that effectively limits where they can be used: Where declarations are allowed (e.g. at the top level or inside a module), and inside value declarations (val and fun), respectively.

      那又如何呢?通常似乎两者都可以使用.例如, Rosetta Stone QuickSort代码可以使用任一种结构,因为有帮助者函数仅使用一次:

      But so what? It often seems that either can be used. The Rosetta Stone QuickSort code, for example, could be structured using either, since the helper functions are only used once:

      (* 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 之间共享非本地声明.例如

      • local ... in ... end is mainly used when you have one or more temporary declarations (e.g. helper functions) that you want to hide after they're used, but they should be shared between multiple non-local declarations. 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.

      If there weren't multiple, you could have used a let ... in ... end instead.

      您总是可以避免使用local ... in ... end来支持不透明的模块(请参见下文).

      You can always avoid using local ... in ... end in favor of opaque modules (see below).

      let ... in ... end主要用于要在函数中一次或多次计算临时结果或解构产品类型(元组,记录)的值的情况.例如

      let ... in ... end is mainly used when you want to compute temporary results, or deconstruct values of product types (tuples, records), one or more times inside a function. 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的一些优点:

      1. 每个函数调用(即使多次使用)也会一次计算绑定.
      2. 绑定可以同时被解构(这里为leftright).
      3. 声明的范围是有限的. (与local ... in ... end相同.)
      4. 内部函数可以使用外部函数的参数,也可以使用外部函数本身.
      5. 相互依赖的多个绑定可以整齐地排列.
      1. A binding is computed once per function call (even when used multiple times).
      2. A binding can simultaneously be deconstructed (into left and right here).
      3. The declaration's scope is limited. (Same argument as for local ... in ... end.)
      4. Inner functions may use the arguments of the outer function, or the outer function itself.
      5. Multiple bindings that depend on each other may neatly be lined up.


      以此类推...实际上,let表达式非常好.


      And so on... Really, let-expressions are quite nice.

      一次使用辅助功能时,最好将其嵌套在let ... in ... end中.

      When a helper function is used once, you might as well nest it inside a let ... in ... end.

      特别是在使用其他原因的情况下也是如此.

      Especially if other reasons for using one applies, too.

      1. (case ... of ...也很棒.)

      1. (case ... of ... is awesome, too.)

      只有一个let ... in ... end时,您可以写例如

      When you have only one let ... in ... end you can instead write e.g.

      fun quicksort [] = []
        | quicksort (x::xs) =
          case List.partition (fn y => y < x) xs of
            (left, right) => quicksort left @ [x] @ quicksort right
      

      这些是等效的.您可能会喜欢其中一种的风格.但是,case ... of ...有一个优点,它也适用于 求和类型 ('a option'a list等),例如

      These are equivalent. You might like the style of one or the other. The case ... of ... has one advantage, though, being that it also work for sum types ('a option, 'a list, etc.), e.g.

      (* 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 ...的一个缺点:模式块不会停止,因此嵌套它们通常需要括号.您也可以通过不同的方式将两者结合起来,例如

      The one disadvantage of case ... of ...: The pattern block does not stop, so nesting them often requires parentheses. You can also combine the two in different ways, e.g.

      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而不是不是无关.

      This isn't so much about not using local ... in ... end, though.

      隐藏在其他地方不会使用的声明是明智的.例如

      Hiding declarations that won't be used elsewhere is sensible. 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
      

      以此类推.基本上,

      1. 如果要重复使用声明(例如函数),请不要隐藏它.
      2. 如果不是,则local ... in ... endlet ... in ... end上的点为空.
      1. If a declaration (e.g. function) will be re-used, don't hide it.
      2. If not, the point of local ... in ... end over let ... in ... end is void.

    • (local ... in ... end没有用.)

    • (local ... in ... end is useless.)

      您永远不想使用local ... in ... end.由于它的工作是将一组帮助程序声明隔离到您的主要声明的子集中,因此这迫使您根据它们所依赖的内容(而不是更期望的顺序)对这些主要声明进行分组.

      You never want to use local ... in ... end. Since its job is to isolate one set of helper declarations to a subset of your main declarations, this forces you to group those main declarations according to what they depend on, rather than perhaps a more desired order.

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

      A better alternative is simply to write a structure, give it a signature and make that signature opaque. That way, all internal declarations can be used freely throughout the module without being exported.

      在j4cbo的 Stilts网站上的SML上,此示例framework 是StaticServer模块:即使结构同时包含两个声明structure U = WebUtilval content_type = ...,它也仅导出val server : ....

      One example of this in j4cbo's SML on Stilts web-framework is the module StaticServer: It exports only val server : ..., even though the structure also holds the two declarations structure U = WebUtil and 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
      

    • 这篇关于“本地"与“本地"之间的区别是:和“让"在SML中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆