如何通过模式匹配从Scala中的列表中删除重复项?

时间:2016-12-27 01:13:49

标签: scala list recursion pattern-matching

作为家庭作业,我必须编写一个能够从列表中删除重复项的函数。它应该是递归的并且具有模式匹配。我不允许使用head,tail,contains等列表函数 对于排序列表,我提出了这个解决方案:

ReadCommited

如何处理未排序的列表?

2 个答案:

答案 0 :(得分:6)

我不会为你做功课,但希望,这会有所帮助。

  1. 你想让你的函数尾递归。这意味着递归调用出现在函数的最后一个位置,这样jvm可以在调用之前清除堆栈中的前一个调用(它使得它执行非常类似于循环,而不需要堆栈上的额外空间) 。在你的原始解决方案中,这样的语句使它不是尾递归的:hd :: remove(tl):你必须调用递归调用,然后hd添加到其结果中。 然后部分打破了尾递归的想法,因为jvm必须在堆栈上记住递归调用完成后返回的位置。
  2. 通常通过将递归作为参数传递函数的最终结果来避免这种情况:

      def remove(u: List[Int], result: List[Int] = Nil): List[Int] = u match {
         case Nil => result
         case a :: b :: tail if a == b => remove(b :: tail, result)
         case head :: tail => remove(tail, head :: result) 
      }
    

    (注意,这里的递归调用都处于尾部位置 - 在调用返回后没有什么可做的,所以在调用递归之前可以从堆栈中清除前一个条目。)

    1. 您需要另一个递归函数 - contains - 它告诉一个给定元素是否包含在列表中。完成后,只需将上面的第二个case子句替换为

      case head :: tail if contains(head, result) => remove(tail, result)
      
    2. 你的工作已经完成!

      1. 如果您想保留列表元素的原始顺序,则需要事后reverse(将case Nil => result替换为case Nil => result.reverse)...如果您不被允许在这里使用.reverse,对你来说这将是另一个很好的练习。你如何递归地反转列表(尾部)?

答案 1 :(得分:1)

我可能会首先对列表进行排序,以便我们可以应用O(n)复杂度模式匹配方法,但是为了保持您需要的顺序索引列表以便以后可以恢复订单,这可以使用zipWithIndex方法完成。此外,由于数据类型从List[Int]更改为List[(Int, Int)],因此您需要在原始数据类型中定义另一个递归删除函数:

def remove(u: List[Int]): List[Int] = {
    val sortedU = u.zipWithIndex.sortBy{ case (x, y) => x}
    def removeRec(su: List[(Int, Int)]): List[(Int, Int)] = {
        su match {
            case Nil => su
            case hd :: Nil => su
            case hd :: hd2 :: tail => {
                if (hd._1 == hd2._1) removeRec(hd2 :: tail)
                else hd :: removeRec(hd2 :: tail)
            }
        }
    }
    removeRec(sortedU).sortBy{case (x, y) => y}.map{ case (x, y) => x}
}

val lst = List(1,2,3,1,3,3)

remove(lst)
// res51: List[Int] = List(2, 1, 3)

注意:这遵循OP的模式,而不是尾递归。如果你想要一个尾递归版本,你可以按照@Dima的好解释。