如何实现TCO的递归

时间:2011-11-23 07:55:35

标签: scala recursion functional-programming scheme tail-call-optimization

我一直在研究递归和TCO。似乎TCO可以使代码冗长并影响性能。例如我已经实现了代码,该代码接收7位数的电话号码并且返回所有可能的单词排列,例如464-7328可以是" GMGPDAS ... IMGREAT ... IOIRFCU"这是代码。

/*Generate the alphabet table*/
  val alphabet = (for (ch <- 'a' to 'z') yield ch.toString).toList

/*Given the number, return the possible alphabet List of String(Instead of Char for convenience)*/
  def getChars(num : Int) : List[String] = {
      if (num > 1) return List[String](alphabet((num - 2) * 3), alphabet((num - 2) * 3 + 1), alphabet((num - 2) * 3 + 2))
      List[String](num.toString)
  }

/*Recursion without TCO*/
  def getTelWords(input : List[Int]) : List[String] = {
    if (input.length == 1) return getChars(input.head)
      getChars(input.head).foldLeft(List[String]()) {
        (l, ch) => getTelWords(input.tail).foldLeft(List[String]()) { (ll, x) => ch + x :: ll } ++ l
      }
  }

它很短,我不必花太多时间在这上面。但是,当我尝试在尾调用递归中执行此操作以获得TCO&#39; ed。我必须花费相当多的时间,而且代码变得非常冗长。我不会为了节省空间而构成整个代码。 Here is a link to git repo link。可以肯定的是,相当多的人可以编写比我更好更简洁的尾递归代码。我仍然认为通常TCO更冗长(例如Factorial和Fibonacci尾调用递归有额外的参数,累加器。)然而,需要TCO来防止堆栈溢出。我想知道你将如何接近TCO和递归。在this thread中使用TCO实现Akermann的方案是我的问题陈述的缩影。

3 个答案:

答案 0 :(得分:5)

你是否有可能使用术语“尾调用优化”,实际上你真的要么意味着以迭代递归方式编写函数,要么继续传递样式,以便所有递归调用都是尾调用? / p>

实施TCO是语言实施者的工作;一篇论文谈论如何有效地完成它是经典的Lambda: the Ultimate GOTO论文。

尾部调用优化是您的语言评估者将为您做的事情。另一方面,您的问题听起来像是在询问如何以特定样式表达函数,以便程序的形状允许评估者执行尾调用优化。

答案 1 :(得分:4)

正如评论中提到的sclv,尾递归对于Haskell中的这个例子来说毫无意义。使用list monad可以简洁有效地编写问题的简单实现。

import Data.Char
getChars n | n > 1     = [chr (ord 'a' + 3*(n-2)+i) | i <- [0..2]]
           | otherwise = ""
getTelNum = mapM getChars

答案 2 :(得分:3)

正如其他人所说,我不会担心这种情况的尾调用,因为与输出的大小相比,它不会非常深入地(输入的长度)递归。在你出局之前你应该没有记忆(或耐心)

我可能会实现类似

的实现
def getTelWords(input: List[Int]): List[String]  = input match {
   case Nil => List("")
   case x :: xs => {
      val heads = getChars(x)
      val tails = getTelWords(xs)
      for(c <- heads; cs <- tails) yield c + cs
   }
}

如果你坚持尾递归,那可能是基于

def helper(reversedPrefixes: List[String], input: List[Int]): List[String] 
  = input match {
    case  Nil => reversedPrefixes.map(_.reverse)
    case (x :: xs) =>  helper(
      for(c <- getChars(x); rp <- reversedPrefixes) yield c + rp,
      xs)
  }

(实际例程应调用helper(List(""), input)