在标准 ml 中定义嵌套函数

Define nested functions in standard ml

我是sml新手。我试图将 int 转换为 int 列表。例如,假设有一个输入1234,那么输出就是一个类似[1,2,3,4]的列表。我的问题是,如何在 sml 中键入嵌套函数?让到底?有我的代码。

 fun digit (a : int): int =

    let
            fun size (a) = if a < 0 then nil
                    else x = Int.toString x then digit s = size(x)

            fun insert (num, nil) = [num]
                    |       insert (num,xs) = x :: insert ()

            fun convert (a, s) = if s < 0 then nil
                            else insert (a / (10*(s - 1)), xs)
                                    then convert(a - (10*(s - 1), s - 1)

    in

    end
fun aFunctionCallingF2F3 someVariables =
   let
       <define some functions and local variables here>
       fun F2 ...
       fun F3 ...
       val v1 ...
       val v2 ...
   in
       <Make use of the functions/variables you defined above and `someVariables`>
   end

例如,

fun areaCirle r:real = 
   let fun square x:real = x*x
       val pi = 3.14
   in 
      pi * square r
   end

或者定义你需要预先调用的函数,如果它们不是相互递归的。如果相互递归,可以查关键字and.

fun F2 ...
fun F3 ...
fun aFunctionCallingF2F3 = <make use of F2 F3 directly>

例如,

fun square x:real = x * x
fun areaCircle r = square r * 3.14

请注意,您不能这样做

fun areaCircle r = square r * 3.14
fun square x:real = x * x

square需要在areaCircle之前定义。

嵌套函数只是将工作量分成多个较小部分的一种方法。另一种选择是 non-nested 库函数。主要区别是非嵌套函数不继承其父变量范围,因此它们只能使用自己的输入,而嵌套函数在其他任何地方都不可用,不能 re-used.假设您要先解决这个问题:

fun digit_meh n = if n < 10 then [n] else n mod 10 :: digit_meh (n div 10)

然后您意识到它并没有完全按照您的意愿进行:

- digit_meh 1234;
> val it = [4, 3, 2, 1] : int list
  • 您可以先删除最高有效位,但计算不像 n mod 10 那样简单,因为它取决于位数。

  • 您可以生成此列表然后反转它:

    fun digit n = rev (digit_meh n)
    

    但是函数 digit_meh 在这个函数之外并不是特别有用,所以可以使用 local-in-end 隐藏它let-in-end:

    local
      fun digit_meh n = if n < 10 then [n] else n mod 10 :: digit_meh (n div 10)
    in
      val digit = rev o digit_meh
    end
    
    fun digit n =
        let fun meh n = if n < 10 then [n] else n mod 10 :: meh (n div 10)
        in rev (meh n) end
    

    请注意函数 mehn 副本遮蔽了 digitn 副本。

    为清楚起见,您还可以为变量命名。

  • 或者您可以看看 rev 是如何做的,然后就去做。它基本上将其输入视为堆栈并递归地将顶部元素放入新堆栈中,以便顶部成为底部,很像 Whosebug 的徽标如果它​​跳出并像 slinky spring 一样颠倒着陆时的样子:

    fun rev L =
        let fun rev_stack [] result = result
              | rev_stack (x::xs) result = rev_stack xs (x::result)
        in rev_stack L [] end
    

    因为结果是在一个额外的参数中累积的,而 rev 应该只接受一个参数,嵌套一个带有额外累积参数的函数是一个非常有用的技巧。

    您也可以模仿这种行为:

    fun digit N =
        let fun digit_stack n result =
                if n < 10
                then n::result
                else digit_stack (n div 10) (n mod 10::result)
        in f N [] end
    

    这样,我们继续首先处理最低有效位,但我们将其放入堆栈 result,这意味着它最终位于底部/末尾。所以我们不需要调用 rev 并保存列表的迭代。

实际上,您不必使用 local-in-endlet-in-end 来隐藏辅助函数;虽然在 let-in-end 的情况下继承父函数的范围很有用,但一旦开始使用带有 opaque signatures 的模块就没有必要隐藏你的函数(:> 运算符):

signature DIGIT =
sig
  val digit : int -> int list
end

structure Digit :> DIGIT =
struct
  fun digit_stack n result =
      if n < 10
      then n::result
      else digit_stack (n div 10) (n mod 10::result)

  fun digit n = digit_stack n []
end

由于这是进入 REPL,只有相关功能在模块外可用:

> structure Digit : {val digit : int -> int list}
  signature DIGIT = {val digit : int -> int list}
- Digit.digit 1234;
> val it = [1, 2, 3, 4] : int list