用尾递归设置交点

我试图使用尾递归和空列表,以产生用于两个集合的交集的溶液[]作为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库中的任何内容。 此外,我必须使用“尾递归”,将累加器作为我随身携带的列表。

这是想法:

  • 检查list1的head元素,如果它存在于list2 ,那么使用“添加了该元素的list1list2和list c尾部”进行递归。 ELSE,然后执行“ list1list2和列表c尾部”(原样)“。

  • 结束条件是列表list1或列表list2是空的或两者都是空的,返回列表c (按原样)。


  • let rec setintersect list list =是错误的:这两个参数的命名应该不同(当然,您应该相应地更新对setintersect2的调用),否则第二个参数将会影响第一个参数。 我会认为OCaml至少会告诉你这个事实,但看起来情况并非如此。

    除此之外,代码看起来有诀窍。 有几件事情可以改善:

  • setintersect本身不是递归的(只有setintersect2是),因此你不需要rec
  • 你应该为setintersect2的参数找到一个不同的名字。 特别是,哪个是累加器并不明显(在这种情况下,大多数OCaml程序员会理解acc或者accu )。
  • c@[h1]效率低下:每次添加元素时都会完全遍历c 。 最好做h1::c并在最后反转结果
  • 作为一个奖励点,如果你在c的开始处追加元素,并且假定a是有序的,那么你不必在调用结束时调用setsimplify :只要检查c是否为空,如果这不是的情况下,只有在h1不等于c的头部时才附加h1

  • 首先,你没有列出你的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
    

    请注意,上面的函数不是尾递归的,我想你可以把它改为尾递归作为消费税。


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

    let intersection l1 l2 = List.filter (fun x -> List.mem x l2) l1
    
    链接地址: http://www.djcxy.com/p/80689.html

    上一篇: Set Intersection with Tail Recursion

    下一篇: convert recursion to 'tail recursion'