递归函数最佳实践;这些是什么?

时间:2009-10-06 18:45:26

标签: language-agnostic functional-programming recursion tail-recursion

除了典型的以外,还有哪些其他语言独立的设计递归函数的方法:

if (counter < 1) 
    return output;
else
   callSelf(); 

是否存在其他方法?每当查看示例时,我总会看到上面代码的版本。

谢谢! :)

8 个答案:

答案 0 :(得分:6)

这就是它。

递归函数设计几乎就像“我可以返回一个值还是需要进一步处理?”一样简单。并且“Processing返回了一个值,在我传递之前我该怎么办?”

尾递归只是编译器/解释器可用于提高性能的优化方法。本质上,如果递归函数遵循严格格式(在递归调用之后没有任何反应,通常意味着递归调用总是与return配对),则可以优化递归函数并将其重写为for循环。

答案 1 :(得分:5)

你的问题究竟是什么? 只是尝试一些答案; - )

有许多类型的递归:

  • “标准”递归

    let rec sum = function
        | [] -> 0
        | x::x' -> x + sum x'
    
  • 尾递归

    let rec sum acc = function
        | [] -> acc
        | x::x' -> sum (x + acc) x'
    
  • 相互递归

     let rec f() = g()
     and g() = f()
    
  • Fixed-Point递归

    let factorial n = fix(fun rec n -> if n < 1 then 1 else n * rec (n - 1)) n
    

递归的应用程序列表非常丰富。在函数式编程中,任何迭代代码(for-loops等)都通过递归来表达!

另一个好例子:

let rec sumTree = function
| End -> 0
| Node(val, right, left) = val + sumTree right + sumTree left

递归的主要“最佳实践”是确保在某个时刻满足您的终止条件,因此您通常会使用比初始调用中更小的数据自我调用函数(只需一部分)的树)。其他一切都会导致无休止的递归。

答案 2 :(得分:3)

嗯,您需要一些方法知道何时停止递归。这就是你的counter < 1,对吗?我经常删除/添加项目到列表,遍历树,并在递归时执行数学函数。最终,您需要知道何时停止递归以及何时不停止递归,因此我没有看到任何其他无法归结为counter < 1的选项。

function ProcessNode(node)
  //Do stuff
  while (node.Next != null)
    ProcessNode(node.Next);

function ManipulateList(list)
  //Do stuff, adding and removing items based on logic
  if (testCondition)
    return;
  else
    ManipulateList(list);

答案 3 :(得分:1)

Google在recursion上提供了大量信息。 :)

答案 4 :(得分:1)

有很多变化,例如:

foreach (child in parameter.GetChildren()) {
   callself(child)
}

switch (param.length) {
   case 1: return param[0];
   case 2: return param[0] + param[1];
   default: return callself(param.FirstHalf()) + callself(param.LastHalf());
}

他们所有人的共同点是,他们将任务分配给较小的任务,然后用自己来解决较小的任务,直到它们太小而无法通过简单的操作来解决。

答案 5 :(得分:1)

在惰性编程语言中,您可以进行不定义端点的递归。结果可能是无限的数据结构,但只要你不尝试使用它,那就没关系。例如,在Haskell中定义整个斐波纳契系列的常用方法是:

fibS = 1:1: zipWith (+) fibS (tail fibS)

这转化为以下英语:斐波纳契数列为1,后跟1,其后是系列,它是斐波那契数列和没有第一个元素的斐波纳契数列的元素和。

这听起来像递归定义,而不是递归函数调用,但在Haskell中没有很大的区别 - 上面只是一个'nullary函数'(一个不带参数的函数)。

通常使用它,你只需要使用fibS的前N个元素。事实上你可以使用所有这些(例如打印全部),只要你对你的程序永远运行感到满意: - )

对于使用“全部”无限递归的更有用的示例,Web服务器可能具有使用不终止的递归函数永久定义的“主循环”。

编辑:如果存在“懒惰”的某些元素,这些原则当然可以应用于其他语言。以下是使用生成器移植到Python的fibS的上述实现:

def zipWith(func, iter1, iter2):
    while True:
        yield func(iter1.next(), iter2.next())

def tail(iter):
    iter.next()
    for x in iter:
        yield x

def fibS():
    yield 1
    yield 1
    for x in zipWith(lambda x,y: x + y, fibS(), tail(fibS())):
        yield x

# Test it by printing out the first n elements.
def take(n, iter):
    while n > 0:
        yield iter.next()
        n = n - 1

print list(take(10, fibS()))

不要指望它与Haskell版本一样高效!您可以使用某种黑客和某种全局对象来提高效率。但请注意缺乏明确的终止条件。

答案 6 :(得分:0)

如果您想要停止递归,则必须进行测试。

但是你可以拥有一些变化的东西,比如河内塔算法(2个递归调用):

Hanoi tower

int Hanoi( source, mid, destination, height ) {

if ( height == 1 ) { print source '->' destination }
else
   Hanoi ( source, destination, mid, height - 1 ) ; # move all upper tower from source to mid
   print source '->' destination;
   Hanoi ( mid , source, destination, height -1 ) ; # move all the upper tower from mid to destination

}
Hanoi ( "0", "1", "2", 8 );

将打印将8张光盘从源移动到dest的解决方案。有关游戏规则,请参阅http://en.wikipedia.org/wiki/Tower_of_Hanoi

答案 7 :(得分:0)

“最佳实践”是尝试使用结构感应(粗略地说,是数据结构的折叠)。如果失败,您可能需要考虑一般(不受限制的)递归。

例如,在处理列表时,通常使用折叠。许多函数,例如连接和追加都很容易以这种方式描述。