【问题标题】:What can blow out my laptop in tail recursion function?什么可以在尾递归功能中炸毁我的笔记本电脑?
【发布时间】:2019-04-10 16:35:25
【问题描述】:

我需要通过索引的奇数和偶数将列表分组为两个列表,从 1 开始,而不是 0。我认为无限递归有问题,因为执行第二个函数的时间太多了而且我的笔记本电脑会去火星。

第一个简单递归的函数可以正常工作,但是第二个,merge2,尾递归让我的电脑崩溃了。

代码如下:

// Simple recursion
def merge1(listA: List[String], listB: List[String]): List[String] = (listA, listB) match {
  case (Nil, Nil) => Nil
  case (head1 :: tail1, Nil) => head1 :: merge1(tail1, Nil)
  case (Nil, head2 :: tail2) => head2 :: merge1(Nil, tail2)
  case (head1 :: tail1, head2 :: tail2) => head1 + head2 :: merge1(tail1, tail2)
}

merge1 (List("a", "b", "c", "d"), List("e", "f", "g", "h", "i", "j"));

// Tail recursion
def merge2 (listA1: List[String], listB1: List[String]): List[String] = {
  def merge2Helper(listA: List[String], listB: List[String], listACC: List[String]): List[String] =
(listA, listB) match {
      case (Nil, Nil) => listACC
      case (head1 :: tail1, Nil) => merge2Helper(tail1, listB, listACC ::: List(head1))
      case (Nil, head2 :: tail2) => merge2Helper(tail2, listB, listACC ::: List(head2))
      case (head1 :: tail1, head2 :: tail2) => merge2Helper(tail1, tail2, listACC ::: List(head1 + head2))
}
  merge2Helper(listA1, listB1, Nil)
}

merge2 (List("a", "b", "c", "d"), List("e", "f", "g", "h", "i", "j"));

【问题讨论】:

  • 使用@annotation.tailrec
  • 第三种情况不应该是:case (Nil, head2 :: tail2) => merge2Helper(listA, tail2, listACC ::: List(head2))?
  • 第三个模式匹配案例case (Nil, head2 :: tail2) => merge2Helper(tail2, listB, listACC ::: List(head2))不应该是case (Nil, head2 :: tail2) => merge2Helper(listA, tail2, listACC ::: List(head2))吗?

标签: scala function functional-programming


【解决方案1】:

第三种情况是错误的,它再次传递了刚刚处理过的同一个列表。

应该是这样的:

@tailrec def merge2Helper(listA: List[String], listB: List[String], listACC: List[String]): List[String] =
(listA, listB) match {
      case (Nil, Nil) => listACC
      case (head1 :: tail1, Nil) => merge2Helper(tail1, listB, listACC ::: List(head1))
      case (Nil, head2 :: tail2) => merge2Helper(listA, tail2, listACC ::: List(head2))
      case (head1 :: tail1, head2 :: tail2) => merge2Helper(tail1, tail2, listACC ::: List(head1 + head2))
}

您还应该添加 @tailrec 注释以提示编译器您希望函数是尾递归的(如果不是,则失败),以确保您不会因长列表而导致堆栈溢出。

【讨论】:

  • 这不是@tailrec 所做的。如果递归是不是尾递归,它只会告诉编译器失败。如果它尾递归那么它没有效果。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-06-30
  • 2018-06-20
  • 1970-01-01
  • 1970-01-01
  • 2020-09-27
  • 1970-01-01
  • 2017-11-29
相关资源
最近更新 更多