2

我在 SML 中有两个列表,比如说 list A[(a,b,c),(d,e,f)]和 list B [b,e]。我想计算 B 中与 A 中每个三元组的第二个元素匹配的每个项目的出现次数。输出应该是 2。因为每个项目在 Abe出现一次。

到目前为止,这是我的代码,但是当我在 B 中从一个元素移动到另一个元素时,我的计数器总是设置为 0。我知道在 Java 中这只是一个简单的双循环。

fun number_in_months (d : (int * int * int ) list, m : (int) list) = 
    if null m then 0 
    else if null d then number_in_months(d, tl m)
    else if (#2(hd d)) = (hd m) then 1 + number_in_months (tl d, m)
    else number_in_months(tl d, m)
4

2 回答 2

5

代码没有在递归调用之间累积值。也可能存在其他逻辑错误。

使用递归和函数累积值是一种常见模式,您可以在此处了解更多信息。其本质是使用headand解构列表,tail直到列表为空并在每次调用时累积一些值。下面的sum函数是一个简单的例子来说明这一点。这可以根据您的示例进行调整accbelist A.

fun sum(numbers: (int) list) =
  let fun sumR(numbers: (int) list, acc: int) =
    if null numbers
    then acc
    else
      sumR(tl numbers, hd numbers + acc)
  in
    sumR(numbers, 0)
  end

运行[1,2,3]给出:

val sum = fn : int list -> int
- sum([1,2,3]);
val it = 6 : int

请注意,我故意对这个答案含糊其辞,因为这是关于 Coursera 编程语言课程的作业的问题。

于 2013-01-15T23:43:28.963 回答
2

正如您所提到的,这将是任何命令式编程语言中的嵌套/双循环。您实际上缺少的是第二个循环。

您的“内部”循环遍历 的所有元素d,完成后,您的“外部”循环会尝试弹出 的顶部元素m并重新开始,从代码的这一行可以看出:

else if null d then number_in_months(d, tl m)

但是,正如您所看到的,您刚刚测试了该列表d为空,并且您将这个(完全相同的列表)提供给您在尾部的递归调用m,然后对于每个连续的调用,这将落在相同的情况下,直到m也是空的并且你返回 0。

因此,您缺少的是原始输入列表的“保留副本” m。这可以通过多种方式完成,但内部(辅助)函数是最常用的函数,它甚至“看起来”像一个嵌套循环

fun number_in_months (d, m) =
    let
      fun nim' ([], y::ys) = nim (d, ys)                 (* 1 *)
        | nim' (_, []) = 0                               (* 2 *)
        | nim' ((_, x2, _) :: xs, yss as (y::ys)) = ...  (* 3 *)
    in
      nim'(d, m)
    end

使用与上述代码匹配的模式变得更加简单且不易出错。在情况 1 中,“内部”循环遍历了 中的所有元素d,因此使用外部函数的递归调用d在任何时候都不会更改。在情况 2 中,“外部”循环遍历了所有元素,m我们返回 0(加法的中性元素)。在案例 3 中,我们进行实际工作。这里使用了模式匹配,这样我们就不需要强制参数的类型,也不需要提取三元组的第二个元素,我们已经在变量中拥有了它x2。所需要做的就是进行计算并使用 and 进行递归xs调用yss

When doing it this way, the inner (helper) function is using a "copy" of the original input list d and stepping through its elements (potentially modifying it), but we always got a reference to the original input list, which we can use if/when needed.

于 2013-01-16T01:28:29.127 回答