Scala递归头尾模式匹配

Scala递归头尾模式匹配,scala,pattern-matching,Scala,Pattern Matching,这是一个示例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但我不明白这是如何工作的,有人能帮我理解递归是如何工作的吗?foo当作为参数ls传递的列表为Nil(空)时终止 每次调用foo,ls都会通过模式匹配提取头部(hd)和尾部(t

这是一个示例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但我不明白这是如何工作的,有人能帮我理解递归是如何工作的吗?

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

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


当计算
foo(7,List(1,2,-2,2))
时,下一步是
1*foo(6,List(2,-2,2))
等等…

因为我不完全确定你在问什么,这可能太复杂了

匹配中

  • case Nil
    将匹配(且仅匹配)一个空列表
  • case hd::tl
    将破坏列表的结构(其工作方式/原因的确切机制超出了本答案的范围),值
    hd
    是列表的第一个元素,值
    tl
    是包含第一个元素之后的每个元素的列表
因此,从评估的替代模型开始,我们得出:

  • foo(7,List(1,2,-2,2))
    :List(
    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

尽管对于
列表
,它将比递归实现慢(如
长度
产品
都将完全遍历列表).

我已将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匹配{
案例无=>
println(s“${printMul(prev)}$n”)
N
案例hd::tl=>
println(s“${printMul(prev)}$hd*foo(${n-1},$tl)”)
hd*foo(n-1,tl,上一个:+hd)
}
//=======输出======
/*
1*foo(6,列表(2,-2,2))
1*2*foo(5,列表(-2,2))
1*2*-2*foo(4,列表(2))
1*2*-2*2*foo(3,List())
1 * 2 * -2 * 2 * 3
*/

这是rec,但不是tailrec for meth还有一些实现是尾部递归的(即编译器将它们转换为循环),并且使用
foldLeft
:两者都需要一个辅助值。