Scala递归头尾模式匹配



这是一个scala代码示例-

def foo(n : Int, ls : List[Int]) : Int =
ls match {
case Nil => n
case hd :: tl => hd*foo(n-1,tl)
}

如果我通过foo(7,List(1,2,-2,2)),则会得到-24,但我不明白这是如何工作的,有人能帮助我理解递归在这里是如何工作吗?

由于我不完全确定你在问什么,这可能过于详细了。

match:中

  • case Nil将匹配(并且仅匹配(一个空列表
  • case hd :: tl将破坏列表(如何/为什么这样做的确切机制超出了这个答案的范围(,其中值hd是列表的第一个元素,值tl是包含第一个元素之后的每个元素的列表

因此,继续使用评估的替代模型,我们最终得出:

  • foo(7, List(1, 2, -2, 2)):列表(ls(不为空,因此第二个match子句匹配,结果为
  • 1 * foo(6, List(2, -2, 2)):列表是非空的,所以第二个match子句匹配,结果是(由于乘法是关联的,所以简化后(
  • 1 * 2 * foo(5, List(-2, 2)):列表不为空,因此第二个match子句匹配,结果为
  • 1 * 2 * -2 * foo(4, List(2)):列表不为空,因此第二个match子句匹配,结果为
  • 1 * 2 * -2 * 2 * foo(3, Nil):列表为空,因此第一个match子句匹配,结果为
  • 1 * 2 * -2 * 2 * 3:乘出去是
  • -24

对于一些人来说,这个函数的逻辑最好表示为

def foo(n: Int, ls: List[Int]): Int =
if (ls.isEmpty) n
else ls.head * foo(n - 1, ls.tail)

经过一点代数运算,它也可以表示为

(n - ls.length) * ls.product

尽管对于将比递归实现慢的List(因为.length.product将各自完全遍历列表(。

我在您的原始答案中添加了println,它打印执行堆栈跟踪,就像Levi在他的答案中解释的那样。这个技巧应该有助于其他递归示例来理解执行流:

def printMul(xs: List[Int]) = if (xs.nonEmpty) xs.mkString(" * ") + " * " else ""
def foo(n: Int, ls: List[Int], prev: List[Int] = Nil): Int =
ls match {
case Nil =>
println(s"${printMul(prev)}$n")
n
case hd :: tl =>
println(s"${printMul(prev)}$hd * foo(${n - 1}, $tl)")
hd * foo(n - 1, tl, prev :+ hd)
}
// ====== Output ======
/*
1 * foo(6, List(2, -2, 2))
1 * 2 * foo(5, List(-2, 2))
1 * 2 * -2 * foo(4, List(2))
1 * 2 * -2 * 2 * foo(3, List())
1 * 2 * -2 * 2 * 3
*/

当作为参数ls传递的列表为Nil(空(时,foo终止。

每次调用foo时,ls都通过模式匹配提取其头部(hd(和尾部(tl(。头与下一个仅取列表尾部的foo调用相乘(以及将n值减去1。

当评估foo(7,List(1,2,-2,2))时,下一步是1 * foo(6, List(2, -2, 2)),依此类推…

最新更新