日常妙招屋
白蓝主题五 · 清爽阅读
首页  > 网络监控

Scala递归函数怎么写

在写网络监控脚本的时候,有时候需要处理嵌套的日志结构或者层层调用的请求链。这时候普通的循环不太够用,就得靠函数自己调用自己——也就是递归。Scala 作为一门函数式编程语言,对递归的支持特别友好,写起来也挺清爽。

递归函数的基本写法

Scala 中写递归函数,最关键的是要定义好终止条件,不然函数会一直调下去,直到爆栈。比如我们想算一个数的阶乘,可以这么写:

def factorial(n: Int): Int = {
  if (n <= 1) 1
  else n * factorial(n - 1)
}

这个函数的意思是:如果 n 小于等于 1,就返回 1,否则就用当前的 n 乘以 factorial(n-1) 的结果。每调一次,n 都在变小,最终会走到 1,结束递归。

用递归遍历树形日志结构

假设你在监控一个微服务系统,每个请求会生成一个请求节点,节点里可能包含多个子请求。这种数据天生就是树状结构。我们可以用递归来一层层钻进去,把所有耗时超过阈值的请求找出来。

case class Request(id: String, duration: Long, children: List[Request])

def findSlowRequests(req: Request, threshold: Long): List[String] = {
  val current = if (req.duration > threshold) List(req.id) else List()
  val fromChildren = req.children.flatMap(child => findSlowRequests(child, threshold))
  current ++ fromChildren
}

这段代码会先判断当前节点是否慢,再递归处理所有子节点,最后把所有超时的请求 ID 汇总起来。整个过程像扫地机器人走迷宫,不漏掉任何一个角落。

尾递归优化避免栈溢出

普通递归在数据很深的时候容易导致 StackOverflowError。Scala 提供了尾递归优化,只要把递归调用放在函数的最后一步,编译器就能把它转成循环,避免爆栈。

还是拿阶乘举例,改写成尾递归版本:

def factorialTail(n: Int, acc: Int = 1): Int = {
  if (n <= 1) acc
  else factorialTail(n - 1, n * acc)
}

这里多了一个参数 acc,用来累积结果。每次递归都把当前结果传下去,最后一刻直接返回,没有额外计算。这样的写法更安全,适合处理大量数据。

在实际做网络请求追踪时,如果调用链特别深,推荐优先使用尾递归,避免程序因为递归太深突然挂掉。