递归匿名函数 Matlab

16

我知道匿名函数不是为此而设计的,但作为一个谜题,我尝试使用匿名函数制作递归函数。递归函数的原型显然是阶乘函数。问题在于,在匿名函数内部很难进行情况区分。到目前为止,我已经做到了以下几点:

f=@(cn,n,f)eval('if n>1; f(cn*n,n-1,f);else;ans=cn;end');
f=@(n)f(1,n,f);

或者,另一种选择:

f=@(cn,n,f)eval('if n>1; f(cn*n,n-1,f);else;disp(cn);end');
f=@(n)f(1,n,f);

令人不太满意的是,在直接赋值时仍然无法使用此函数,a=f(3) 仍然会产生错误,因为 eval 没有得到一个值。

所以我的问题是,你是否可以通过匿名函数来实现递归函数,例如以一种允许例如 a=f(3) 的方式计算阶乘,只依靠本机 matlab 函数(或者像我在示例中创建的函数)?

PS: 我知道这没有任何实际用途,它只是对你可以如何弯曲和滥用 Matlab 语法的挑战。


1
你是指匿名函数吗?对我来说,函数句柄似乎是一个更广泛的对象类别。但是我不是专家,所以我真的在问。 - Andras Deak -- Слава Україні
@AndrasDeak 当然,我知道,谢谢你指出来! - flawr
@patrik 试过了,但没有帮助:) 问题是,如果你只调用f(3),它会执行if...,一切都好。但是如果你想要赋值a=f(3),它会想要做类似a= if...的操作。我们在Matlab中非常需要一个条件三元运算符;) - Andras Deak -- Слава Україні
7
这可能会有帮助:这篇文章 - beaker
@AndrasDeak 我现在做了! - flawr
显示剩余5条评论
1个回答

11

我们现在找到了两个可能性,都依赖于使用单元数组。请注意,这在Octave中可能无法正常工作。

关键是实现一个情况区分的方法。我发现的第一个方法可以在这里找到。

此方法利用了Matlab的布尔值,其中true可以评估为1,而false则可以评估为0

if_ = @( pred_, cond_ ) cond_{ 2 - pred_ }();

在这里,我们需要提供一个条件作为第一个参数,以及一个2元素单元格数组作为第二个参数。每个单元格元素都应该是一个函数句柄,如果条件为真/不真,则调用该函数。我们的阶乘函数应该像这样:

fac = @(n,f)if_(n>1,{@()n*f(n-1,f),@()1})
factorial_=@(n)fac(n,fac);
factorial_(10)

如下@AndrasDeak的评论所述:这里重要的部分是我们有一个单元格数组,其中包含函数而不是。这提供了短路,因为只有当我们调用相应的函数@()n*f(n-1,f)时,才会评估n*f(n-1,f)

第二种方法由@beaker发现,它具有更大的灵活性:

iif = @(varargin) varargin{2*find([varargin{1:2:end}], 1, 'first')}();

这是利用匿名函数当中可以使用varargin(可变数量的参数)的特性。调用此函数时,您必须交替指定条件和如果条件为真应该执行的内容。这个方法甚至允许使用switch结构或者if ... else if ... else if ... (...) else ...结构。当被调用时,它将查找第一个为真的条件(find([varargin{1:2:end}], 1, 'first')),并调用相应的函数。我们阶乘函数的示例如下:

fac = @(n,f)iif(n>1,@()n * f(n-1,f),true,@()1);
factorial_=@(n)fac(n,fac);
factorial_(10)

编辑:有趣的事实是,我们正在做的事情与这行代码没有直接关系。

 factorial_=@(n)fac(n,fac);

也被称为应用不动点组合子。实际上我们可以写成:

 Y = @(f)@(x)f(x,f);
 factorial_=Y(f);

1
我建议在两种情况下都强调需要使用@()来保护参数免受评估。例如,当您调用mean(rand(3))时,Matlab首先评估rand(3),然后将该矩阵传递给mean函数。如果省略@()部分,则iif将尝试评估无意义的函数。主要观点:iif函数似乎在第一个true参数处短路,但实际上并非如此。 - Andras Deak -- Слава Україні

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接