是否有等效于 C 中的嵌套递归函数?
Posted
技术标签:
【中文标题】是否有等效于 C 中的嵌套递归函数?【英文标题】:Is there an equivalent to a nested recursive function in C? 【发布时间】:2015-04-21 15:42:13 【问题描述】:首先,我知道 C 标准不支持嵌套函数。
但是,在其他语言中,定义一个辅助递归函数以利用外部函数提供的数据通常非常有用。
这是一个例子,用 Python 计算 N-queens problem 的解数。例如,在 Lisp、Ada 或 Fortran 中编写相同的代码很容易,它们都允许某种嵌套函数。
def queens(n):
a = list(range(n))
u = [True]*(2*n - 1)
v = [True]*(2*n - 1)
m = 0
def sub(i):
nonlocal m
if i == n:
m += 1
else:
for j in range(i, n):
p = i + a[j]
q = i + n - 1 - a[j]
if u[p] and v[q]:
u[p] = v[q] = False
a[i], a[j] = a[j], a[i]
sub(i + 1)
u[p] = v[q] = True
a[i], a[j] = a[j], a[i]
sub(0)
return m
现在我的问题是:有没有办法在 C 中做 类似的事情?我会想到两种解决方案:使用全局变量或将数据作为参数传递,但它们看起来都不太令人满意。
还有一种方法可以把这个写成迭代程序,但是比较笨拙:其实我先在Fortran 77为Rosetta Code写了迭代解决方案,然后就想把这个烂摊子整理一下。 Fortran 77 没有递归函数。
对于那些想知道的人,该函数将 NxN 板管理为 [0, 1 ... N-1] 的排列,因此皇后在行和列上是单独的。该函数正在寻找也是问题解决方案的所有排列,开始检查第一列(实际上没有要检查的内容),然后是第二列,并且仅当第一个 i
列处于有效配置时才递归调用自身。
【问题讨论】:
一些 C 实现,例如GCC,允许嵌套函数作为扩展。但通常的做法是将数据作为参数传递。 Is there a a way to achieve closures in C的可能重复 @Barmar 我已经阅读了 GCC 的这个特性,但我不会使用这样的非标准特性。我也认为正常的方法是你建议的,但我想知道是否有更聪明的方法。 如果它更容易,您可以将数据作为(指向 a)结构的指针传递。这里的优点是美观,但可能更令您满意。 谢谢@Matt 我会看看这个。虽然可能有一个技巧,因为数据必须在某个地方,但我猜 Ada 在内部也使用了一个技巧。我必须检查程序集输出。 【参考方案1】:当然。您需要模拟嵌套函数使用的特殊环境,作为模块级别的静态变量。在嵌套函数上方声明它们。
为了不把事情搞砸,你把整个事情放到一个单独的模块中。
【讨论】:
是的,我什至不应该问,尽管我可能错过了 C 的一个特性,因为我不是专家。闭包显然没有魔法,数据必须可以通过指针访问,在汇编程序中,该指针必须是常量(=全局变量),或者基于堆栈(=参数)或基于寄存器。 gfortran 和 gnat 使用最后一个选项。我从来没有考虑过闭包的实现,但是一旦看到汇编器的样子,就会更容易找到。 @Jean-ClaudeArbaut 好吧,对于闭包,你真的应该将所有相关的东西打包成一些结构并传递它,当然是通过引用。 解释这些其他语言隐含地在做什么。数据在递归期间散布在堆栈上,您只需将其放在堆上即可。我上面描述的是最基本的方法,顺便说一下,重构会很困难。 @Jean-ClaudeArbaut 我自己,我更多来自 Lisp 世界;完全闭包非常复杂(查看"funarg problem",这导致需要维护堆栈帧的树,而不仅仅是列表)。希望您不需要完全的复杂性。【参考方案2】:编者注:这个答案是从问题编辑的内容中移来的,它是由原始海报撰写的。
感谢大家的建议。这是使用作为参数传递的结构的解决方案。这大致相当于 gfortran 和 gnat 在内部处理嵌套函数的做法。顺便说一下,参数i
也可以在结构中传递。
内部函数声明为静态,以帮助编译器优化。如果它不是递归的,那么代码可以集成到外部函数中(在一个简单的示例中使用 GCC 进行测试),因为编译器知道该函数不会从“外部”调用。
#include <stdio.h>
#include <stdlib.h>
struct queens_data
int n, m, *a, *u, *v;
;
static void queens_sub(int i, struct queens_data *e)
if(i == e->n)
e->m++;
else
int p, q, j;
for(j = i; j < e->n; j++)
p = i + e->a[j];
q = i + e->n - 1 - e->a[j];
if(e->u[p] && e->v[q])
int k;
e->u[p] = e->v[q] = 0;
k = e->a[i];
e->a[i] = e->a[j];
e->a[j] = k;
queens_sub(i + 1, e);
e->u[p] = e->v[q] = 1;
k = e->a[i];
e->a[i] = e->a[j];
e->a[j] = k;
int queens(int n)
int i;
struct queens_data s;
s.n = n;
s.m = 0;
s.a = malloc((5*n - 2)*sizeof(int));
s.u = s.a + n;
s.v = s.u + 2*n - 1;
for(i = 0; i < n; i++)
s.a[i] = i;
for(i = 0; i < 2*n - 1; i++)
s.u[i] = s.v[i] = 1;
queens_sub(0, &s);
free(s.a);
return s.m;
int main()
int n;
for(n = 1; n <= 16; n++)
printf("%d %d\n", n, queens(n));
return 0;
【讨论】:
以上是关于是否有等效于 C 中的嵌套递归函数?的主要内容,如果未能解决你的问题,请参考以下文章