如何实现 TCO ed 递归
我一直在研究递归和 TCO。看来 TCO 会使代码变得冗长并且还会影响性能。例如,我已经实现了接受 7 位电话号码并返回所有可能的单词排列的代码,例如 464-7328 可以是“GMGPDAS ... IMGREAT ... IIORFCU” 这是代码。
/*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 时。我必须花费大量时间并且代码变得非常冗长。为了节省空间,我不会给出整个代码。 这里是 git repo 链接。可以肯定的是,你们中的很多人都可以编写比我更好、更简洁的尾递归代码。我仍然认为,一般来说,TCO 更为冗长(例如阶乘和斐波那契尾部调用递归有额外的参数,累加器)。但是,需要 TCO 来防止堆栈溢出。我想知道您如何处理 TCO 和递归。 此线程中 Akermann 与 TCO 的方案实现概括了我的问题陈述。
I have been looking into recursion and TCO. It seems that TCO can make the code verbose and also impact the performance. e.g. I have implemented the code which takes in 7 digit phone number and gives back all possible permutation of words e.g. 464-7328 can be "GMGPDAS ... IMGREAT ... IOIRFCU" Here is the code.
/*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
}
}
It is short and I don't have to spend too much time on this. However when I try to do that in tail call recursion to get it TCO'ed. I have to spend a considerable amount of time and The code become very verbose. I won't be posing the whole code to save space. Here is a link to git repo link. It is for sure that quite a lot of you can write better and concise tail recursive code than mine. I still believe that in general TCO is more verbose (e.g. Factorial and Fibonacci tail call recursion has extra parameter, accumulator.) Yet, TCO is needed to prevent the stack overflow. I would like to know how you would approach TCO and recursion. The Scheme implementation of Akermann with TCO in this thread epitomize my problem statement.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您是否有可能使用术语“尾部调用优化”,而实际上您实际上是指以迭代递归风格或连续传递风格编写函数,以便所有递归调用都是尾部调用?
实现 TCO 是语言实现者的工作;一篇讨论如何有效完成的论文是经典的 Lambda:终极 GOTO 论文。
尾部调用优化是您的语言的评估器将为您做的事情。另一方面,您的问题听起来像是在问如何以特定的风格表达函数,以便程序的形状允许您的评估器执行尾部调用优化。
Is it possible that you're using the term "tail call optimization", when in fact you really either mean writing a function in iterative recursive style, or continuation passing style, so that all the recursive calls are tail calls?
Implementing TCO is the job of a language implementer; one paper that talks about how it can be done efficiently is the classic Lambda: the Ultimate GOTO paper.
Tail call optimization is something that your language's evaluator will do for you. Your question, on the other hand, sounds like you are asking how to express functions in a particular style so that the program's shape allows your evaluator to perform tail call optimization.
正如 sclv 在评论中提到的,尾递归对于 Haskell 中的这个例子来说是没有意义的。使用列表 monad 可以简洁有效地编写问题的简单实现。
As sclv mentioned in the comments, tail recursion is pointless for this example in Haskell. A simple implementation of your problem can be written succinctly and efficiently using the list monad.
正如其他人所说,我不会担心这种情况下的尾部调用,因为与输出的大小相比,它不会递归得很深(输入的长度)。在堆栈溢出之前,您应该耗尽内存(或耐心),
我可能会使用类似的方法来实现
如果您坚持使用尾递归,这可能基于
(实际例程应该调用
helper(List (“”),输入)
)As said by others, I would not be worried about tail call for this case, as it does not recurse very deeply (length of the input) compared to the size of the output. You should be out of memory (or patience) before you are out of stack
I would implement probably implement with something like
If you insist on a tail recursive one, that might be based on
(the actual routine should call
helper(List(""), input)
)