设置尾部递归的交集

时间:2014-03-03 10:52:12

标签: recursion ocaml tail-recursion

我正在尝试使用尾递归和空列表[]作为accu为两个集合的交集生成解决方案:

let rec setintersect list list =
  let rec setintersect2 a b c = 
    match a with
      | [] -> (match b with [] -> (setsimplify c) | h::t -> (setsimplify c))
      | h1::t1 -> (match b with [] -> (setsimplify c) |h2::t2 -> (if (elementof h1 b) then (setintersect2 t1 b (c@[h1])) else (setintersect2 t1 b c)))  in
  setintersect2 list list [];;

Elementof接受“一个int和一个列表”并正确地工作,如果x是列表的元素,则给出true,否则为false。

问题在于:

# setintersect [5;2;1] [2;6;9];;
- : int list = [2; 6; 9]

它应该给[2]

我做错了什么? 我觉得有些事情很简单,我误解了!

编辑: 感谢到目前为止的回复。

setsimplify只删除重复项。 因此[2,2,3,5,6,6]变为[2,3,5,6]。经过测试并确保其正常运行。

我也不应该使用List库中的任何东西。另外,我必须使用“tail recursion”,累加器是我建立的列表。

以下是这个想法:

  • 检查list1中的head元素,如果它存在于list2中,那么使用“list1的尾部,list2和列表{{进行递归1}}添加了该元素“。 ELSE,然后使用“clist1的尾部和列出list2(原样)”来递归“。

  • 结束条件为clist1为空或两者都为空,返回列表list2(原样)。

2 个答案:

答案 0 :(得分:1)

let rec setintersect list list =是错误的:两个参数应该以不同的方式命名(你当然应该相应地更新对setintersect2的调用),否则第二个会影响第一个。我本以为OCaml至少会警告你这个事实,但似乎事实并非如此。

除此之外,代码似乎可以解决问题。有一些事情可以改进:

  • setintersect本身不是递归的(只有setintersect2),因此您不需要rec
  • 您应该为setintersect2的参数找到不同的名称。特别是,在这些情况下,大多数OCaml程序员都会理解累加器(accaccu是不明显的。)
  • c@[h1]效率低下:每次追加元素时,您都会完全遍历c。最好做h1::c并在结尾处反转结果
  • 作为奖励点,如果您在c的开头添加元素,并假设订购了a,则无需在结尾处调用setsimplify致电:只需检查c是否为空,如果不是这样,只有在h1不等于c的头部时才附加{{1}}。

答案 1 :(得分:0)

首先,您没有列出您的setsimplify功能。

要编写ocaml函数,请先尝试拆分,然后尽可能合并。

要解决此任务,您只需浏览l1中的所有元素,并为每个元素检查它是否在l2中,对吧?

所以你肯定需要一个函数来检查元素是否在列表中,对吗?

让一个人:

let rec mem x = function
  | [] -> false
  | hd::tl -> hd = x || mem x tl

然后你可以做你的交集:

let rec inter l1 l2 =
  match l1 with
    | [] -> []
    | hd::tl -> if mem hd l2 then hd::(inter tl l2) else inter tl l2

请注意,上面的函数不是尾递归的,我猜你可以将它改为tail-recursive作为消费税。


如果你使用std库,那很简单:

let intersection l1 l2 = List.filter (fun x -> List.mem x l2) l1