【发布时间】:2014-01-04 11:11:30
【问题描述】:
在用 C 实现词法分析器时,我发现自己正在编写递归代码:
// Return a List of Tokens
List Lexer_run(const char* input) {
if(input[0] == '\0') {
return new_List();
} else {
// ... Find Token and possibly advance in input for a few characters
return List_add(Lexer_run(nextInput), newToken);
}
考虑另一个链表实现的例子
List_length(List* this) {
if(!this) {
return 0;
} else {
return 1 + List_length(this->next);
}
}
我想知道我是否总是可以在 C 中使用这样的递归代码,或者我是否应该避免它,除非是这种情况 确实需要递归(例如递归下降解析器或树结构)
我目前的想法
递归的优点:
- 可读且优雅
缺点:
- 会迅速导致堆栈溢出(在我的计算机中大约有 1'000'000 次调用)
- 与迭代版本相比可能效率低下
解决方案:
- 使用尾调用优化并让编译器将我的递归转换为循环,但我发现尾调用代码的可读性较差。
- 为我的程序增加堆栈大小
注意
我的问题不是专门针对我的示例,而是一个一般性问题,是否应该使用 C中的递归。
【问题讨论】:
-
要回答这篇文章中唯一的问题是:不,避免它,除非它带来了重要除了更少的击键。
-
“我想知道我是否总是可以在 C 中使用这样的递归代码” - 你总是可以使用递归,因为 C 允许任何函数调用自己(甚至是
main())。如果递归代码比迭代代码更易读,我们也鼓励您编写递归代码。是的,递归可能会更慢。是的,它会导致堆栈溢出。这并不总是很重要——不要过早优化。 -
@WhozCraig 我不同意。这不是关于“更少的击键”。递归可以更容易理解(如果我不在一个每秒计算数十亿个浮点数的紧密循环中,我并不真正关心如果代码更具可读性和递归丢失的“效率”更容易掌握。)
-
没有什么规定递归函数必须对所有任务使用递归——例如函数解析xml 数据甚至递归下降解析器可以而且应该在适当的情况下使用迭代和递归控制机制。
-
@H2CO3 你还会使用尾递归还是普通的?