我在SML中有两个列表,比如列表A [(a,b,c),(d,e,f)]
和列表B [b,e]
。我想计算B中每个项目与A中每个三元组的第二个元素匹配的次数。输出应为2.因为b
和e
每个在A中出现一次。
这是我的代码到目前为止,但是当我从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)
答案 0 :(得分:5)
代码不会在递归调用之间累积值。可能还有其他逻辑错误。
使用递归和函数累积值是一种常见模式,您可以阅读有关here的更多信息。它的本质是使用head
和tail
解构列表,直到列表为空并在每次调用时累积一些值。下面的sum
函数是一个显示此功能的简单示例。这可以根据您的示例进行调整,以便在acc
中找到b
或e
时累积list 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作业的问题。
答案 1 :(得分: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
中使用了它。所需要的只是进行计算并使用xs
和yss
进行递归调用。
当这样做的时候,内部(帮助器)函数正在使用原始输入列表d
的“副本”并逐步执行其元素(可能会修改它),但是我们总是引用它原始输入列表,我们可以在需要时使用它。