函数句柄内的逻辑短路

Logical short-circuit inside a function handle

我有一个对任意大小的二维数组进行操作的函数句柄:

R2T = @(DL1,DL2) arrayfun(@(DL1,DL2)...
                 1/(fzero(@(x)fFitObj1(x)./fFitObj2(x)-...
                 DL1./DL2,[minLim maxLim])) ...
                 ,DL1,DL2) - C1;

以下是其功能的自下而上的细分:

我的问题是有时矩阵 DL1, DL2 可能包含 NaN 个值,在这种情况下 fzero return 出现以下错误:

Error using fzero (line 242)
Function values at interval endpoints must be finite and real.

这就是为什么我很自然地想到可用的 operations with short-circuiting,所以我尝试将 any(isnan([DL1,DL2])) 合并到其中,这样 fzero 甚至不会被评估,如果它的输入会是 NaN - 但无论我尝试什么(例如定制的三元运算符), fzero 似乎都被评估并且代码错误。

期望的结果: 我想实现 fzero 的惰性评估,仅在输入有效时发生(在这种情况下,不是 NaN) 和 return NaN 否则如下面的 Edit 所示。

相关资源:


编辑:

这是一段说明问题的代码 (MATLAB 2014a):

clear variables; clc;

LIM = [0 5];
fFitObj1 = @(x)x.^2; fFitObj2 = @(x)1;
C1 = 100;

[DL1A,DL2A,DL1B] = deal(ones(2));
DL1B(4) = NaN; DL2B = DL1B;

R2T = @(DL1,DL2) arrayfun(@(DL1,DL2)...
                 1/(fzero(@(x)fFitObj1(x)./fFitObj2(x)-...
                 DL1./DL2,LIM)) ...
                 ,DL1,DL2) - C1;
             
R2T(DL1A,DL2A) %//case A, runs fine
%{
// ans =
// 
//   -99   -99
//   -99   -99
%}   
R2T(DL1B,DL2B) %//case B, errors due to NaN
%{
// Error using fzero (line 242)
// Function values at interval endpoints must be finite and real.
// 
// Error in @(DL1,DL2)1/(fzero(@(x)fFitObj1(x)./fFitObj2(x)-DL1./DL2,LIM))
//
//
// Error in @(DL1,DL2)arrayfun(@(DL1,DL2)1/(fzero( .....
%}

在案例 B 中,期望的结果是:

 ans =
 
   -99   -99
   -99   NaN

如前所述:这样做内联是疯狂,你最好使用一个单独的函数/.m文件。

会是

  • 更快
  • 更容易阅读
  • 更容易写
  • 更容易调试

例如,您可以采用与此类似的方式执行此操作:

function out = R2TComputation(DL1, DL2, minLim, maxLim, C1)
...%Compute whatever R2T would compute.

要获得与原始匿名函数相同的接口,您只需创建

R2T = @(DL1, DL2) R2TComputation(DL1, DL2, minLim, maxLim, C1)

这将在您创建此句柄时捕获 minLimmaxLimC1 的当前值 R2T


另一种选择是使用 nested function 而不是外部的。它可以访问父函数的变量,但仍然可以使用 ifelse 和您需要的所有其他基本工具。唯一的缺点:不能从其他文件中访问它。

... % Main function stuff
     function out = R2T(DL1, DL2)
         if ...
            out = ...
         ...
     end
... % Use R2T ...

不过,为了搬起石头砸自己的脚,这里是if-else的内联版本,我本着Loren's blog post的精神写的不建议使用 ,因为使用单个表达式而不是相应的 if-else 语句几乎没有任何好处。

ifelse = @(cond, varargin) varargin{1+~cond}(); %Only for the insane

如果你想让它用零参数做lazy evaluation, you need to pass an anonymous function,然后ifelse会计算(这就是ifelse中最后两个括号()的用途) :

ifelse(true, 42, @()disp('OMG! WTF! THIS IS CRAZY!!111'))

如果您只是将对 disp 的函数调用编写为 ifelse 的参数而没有 @(),则该函数甚至会在我们访问 ifelse 之前被调用。这是因为 MATLAB(与大多数其他语言一样)首先计算函数的 return 值,然后将其作为参数传递给 ifelse

在您的情况下,结果代码将是:

R2T = @(DL1,DL2) arrayfun(@(DL1,DL2)...
                 ifelse(~any(isnan([DL1, DL2])), ...
                        @() 1/(fzero(@(x)fFitObj1(x)./fFitObj2(x)-DL1./DL2,LIM)), ...
                        NaN), ...
                 DL1, DL2) - C1;