【问题标题】:let val declarations in SML NJ让 SML NJ 中的 val 声明
【发布时间】:2016-03-22 03:46:13
【问题描述】:

我在理解它的工作原理时遇到了心理障碍,希望我能得到一些指导。下面的函数 f 将对整数列表进行排序(f [1,2,3])。我坚持的部分是 val 声明和递归中发生的事情。我知道 val 声明将允许我将列表中的第二个值与第一个值进行比较,但我对列表连接感到困惑。似乎该函数只会比较前两个值,然后添加列表的其余部分(即 x :: y :: ys)。我不确定 f ls 实际上是如何工作的。难道是……

1) 比较前两个值并添加到列表中

2) 那么 f ls 被递归调用了吗?

似乎是这样,但后来我对“ys”感到困惑-似乎每次通话都会添加列表的末尾。我知道排序有效,但不确定它实际上是如何工作的。

fun f ([x]) = [x]
|   f(x :: ls) =

let 
    val (y :: ys) = f ls
in
    if y > x then x :: y :: ys
    else
        y :: x :: ys
end

编辑 - 再想一想,in / end 主体中的y::ys 实际上是递归调用的吗?如果是这样,SML 足够聪明,知道如果遇到 else,它应该在 y::ys 位置使用 x::ys

【问题讨论】:

    标签: smlnj


    【解决方案1】:

    首先,当列表非常随机时,排序不起作用,例如f [2,3,4,1,2,4,6,0,6,7]

    其次,回答您关于此特定功能如何工作的问题, 您可以通过在代码中添加以下 print 来轻松地对其进行可视化:

    fun f ([x]) = [x]
    |   f(x :: ls) = (print( (Int.toString (x)) ^ "\n");
    
    let 
        val (y :: ys) = f ls
        val x_str = Int.toString (x)
        val y_str = Int.toString (y)
        val ys_str = concat (map Int.toString ys);
        val y_gt_x = if y > x then " ---> this one applies " else ""
        val x_gt_y = if x >= y then " ---> this one applies " else ""
    in
        (print ("if " ^ y_str ^ " > " ^ x_str ^ 
                "\n      then " ^ x_str ^ " :: " ^ y_str ^ ys_str ^ y_gt_x ^ 
                "\n      else " ^ y_str ^ " :: " ^ x_str ^ ys_str ^  x_gt_y ^ "\n");
        if y > x then x :: y :: ys
        else
            y :: x :: ys)
    end) 
    

    上面的随机列表输出如下:

    - f [2,3,4,1,2,4,6,0,6,7];
    2
    3
    4
    1
    2
    4
    6
    0
    6
    if 7 > 6
          then 6 :: 7 ---> this one applies
          else 7 :: 6
    if 6 > 0
          then 0 :: 67 ---> this one applies
          else 6 :: 07
    if 0 > 6
          then 6 :: 067
          else 0 :: 667 ---> this one applies
    if 0 > 4
          then 4 :: 0667
          else 0 :: 4667 ---> this one applies
    if 0 > 2
          then 2 :: 04667
          else 0 :: 24667 ---> this one applies
    if 0 > 1
          then 1 :: 024667
          else 0 :: 124667 ---> this one applies
    if 0 > 4
          then 4 :: 0124667
          else 0 :: 4124667 ---> this one applies
    if 0 > 3
          then 3 :: 04124667
          else 0 :: 34124667 ---> this one applies
    if 0 > 2
          then 2 :: 034124667
          else 0 :: 234124667 ---> this one applies
    val it = [0,2,3,4,1,2,4,6,6,7] : int list
    -
    

    如您所见,函数flet 绑定部分递归调用自身。 (检查代码: let val (y :: ys) = f ls

    如您所见,f ls 是函数 f 的递归调用,因此您对 y :: ysin 主体内的递归调用的分析是不正确的,因为这只是在元素y 到列表ys)

    in 正文不会被评估,除非一直位于列表末尾。因此in 正文将首先评估列表的最后两个元素,例如7 > 6 并相应地增加列表 ys

    第三,回答第一点为什么排序功能f不能正常工作是因为它不断向ys添加新元素,而没有查看新元素是否在ys中按顺序放置关于其余元素。是的,列表ys 的第一个元素与要添加的新元素进行比较,因此两者中最大的元素将首先添加到ys 的其余部分,但这并不能保证正确的位置到ys的第二个和第三个等元素。

    【讨论】:

    • 感谢您抽出宝贵时间进行设置和解释!所以我很清楚 1)然后递归调用首先操作以将列表拆分为元素进行比较? 2) 随着 ys 的增长,它是新的列表吗?
    • 或多或少,更准确地说:递归调用将列表中的每个元素解除绑定到堆中,并且在列表的末尾,堆被回溯,每个单独的元素被相应地挖掘和比较然后将这些元素插入到 ys 中。我建议您使用我提供的简单输入和更复杂输入的代码,并理解为什么首先只输出数字,然后只进行比较。
    • 我会感谢的。这非常有帮助,所以感谢您为我整理这些内容。我一直在努力理解这是如何工作的,但这应该会有所帮助
    猜你喜欢
    • 1970-01-01
    • 2010-10-23
    • 1970-01-01
    • 2011-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多