【问题标题】:Scala: Tail Recursion and ListBufferScala:尾递归和 ListBuffer
【发布时间】:2014-01-14 20:31:49
【问题描述】:

我正在学习 Scala,在此过程中,我遵循了 Brien (http://www.knowing.net/index.php/2006/06/16/15-exercises-to-know-a-programming-language-part-1/) 的 15 个练习。在第二个练习中,我应该实现 Haar 转换。我实现了大部分 但是在尾递归的返回值上挣扎了几个小时。由于编译器不编译 ++ - 或者更确切地说是 haar(averages) ++ haar(averagesD) 行。

  • 我在递归函数中做错了什么?
  • 您能否就我的代码提供其他反馈?

代码:

import scala.collection.mutable.ListBuffer
import scala.annotation.tailrec

object haarWavelet2 {

  def avg(tpl:Tuple2[Double, Double]):Double = (tpl._1 + tpl._2) / 2.
  def avgD(tpl:Tuple2[Double, Double]):Double = (tpl._1 - tpl._2) / 2
  def total_avg(nums:ListBuffer[Double]):Double = nums.sum / nums.length

  @tailrec 
  def haar(nums:ListBuffer[Double]):ListBuffer[Double] = {

    if (nums.length == 1) {return nums}

    val buffer = new ListBuffer[Tuple2[Double, Double]]
    for (i <- 0 to nums.length-1 by 2) buffer.append((nums(i), nums(i+1)))

    val averages = for(tpl <- buffer) yield avg(tpl)
    val averagesD = for(tpl <- buffer) yield avgD(tpl)

    haar(averages) ++ haar(averagesD) // does not compile
    }

  def main(args: Array[String]): Unit = {
          print(haar(ListBuffer(8., 5., 6., 2.)))
  }
}

【问题讨论】:

  • 我在递归函数中做错了什么?您正在执行 tail 递归。根据定义 tail 递归调用必须是要评估的 last 一个语句,但在您的情况下,最后一个语句是 ++ (进行递归调用,实际上是两个他们,然后才处理结果)
  • 有什么理由在这里使用可变的ListBuffer?递归函数对我来说是“不可变的”。 Vector 的追加速度非常好。

标签: scala tail-recursion


【解决方案1】:

尾递归的形式如下:

def func(x..., value){
  if(condition) return value
  else func(y..., value')
}

如果您查看此表单,您会看到,为了评估 func,我需要的只是 func 本身,但具有一组不同的参数。因此,栈上只有一项,可以很容易地转化为迭代算法。

您实现的内容如下所示:

def func(x...){
  if(condition) return value
  else func(y...) + func(z...)

请注意,为了评估func,您必须首先评估func、运算符+,然后再次“func”。所以这 3 个项目需要以非常真实的评估顺序放置在堆栈上,这不适合尾调用优化。

【讨论】:

    【解决方案2】:

    按照上面的@wheaties 结构,尾递归函数如下所示,

    @tailrec 
    def haar(nums:ListBuffer[Double]): ListBuffer[Double] = {
    
        def haarAcc (nums:ListBuffer[Double], acc:ListBuffer[Double]): ListBuffer[Double] = {
          if (nums.length == 1) return acc
    
          // val nums_updated ...
    
          haarAcc(nums_updated, averages ++ averagesD) 
        }
    
        haarAcc(nums, ListBuffer())
    }
    

    【讨论】:

    • 为了确保终止,将haarAcc 中的nums.length 定义为连续调用单调递减。
    猜你喜欢
    • 1970-01-01
    • 2018-03-17
    • 2018-03-08
    • 1970-01-01
    • 1970-01-01
    • 2017-04-10
    • 2014-12-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多