功能句柄内部的逻辑短路

Dev*_*-iL 6 matlab anonymous-function lazy-evaluation short-circuiting logical-operators

我有一个函数句柄,可以在任意大小的2d数组上运行:

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

这是一个自下而上的细分:

  • fzero(@(x)fFitObj1(x)./fFitObj2(x)-DL1./DL2,[minLim maxLim])- 该位在间隔上查找所考虑函数的零[minLim maxLim],其中fFitObj1fFitObj2是之前可用的函数句柄,C1是一些已知的常量并且DL1, DL2被提供.
  • @(DL1,DL2)1/(fzero(...))- fzero允许DL1DL2从外部提供的包装.
  • arrayfun(@(DL1,DL2)...,DL1,DL2)- 另一个包装器,允许fzeroDL1, DL2作为矩阵提供时逐个元素地正确操作.
  • R2T = @(DL1,DL2) arrayfun(...) - C1;- 另一个允许DL1, DL2从外部提供的包装器.

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

Error using fzero (line 242)
Function values at interval endpoints must be finite and real.
Run Code Online (Sandbox Code Playgroud)

这就是为什么我自然想到了可用的具有短路操作,所以我试图并入any(isnan([DL1,DL2]))到这一点,以便fzero甚至不会,如果它的投入将是评估NaN-但无论我尝试(例如,一个特制的三元运算符)在fzero好像被评估和代码中的错误.

期望的结果:我想实现fzero对输入有效的懒惰评估(在这种情况下,不是NaN),NaN否则返回,如下面的编辑所示.

相关资源:


编辑:

这是一段代码来说明问题(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( .....
%}
Run Code Online (Sandbox Code Playgroud)

期望的结果,如果B是:

 ans =

   -99   -99
   -99   NaN
Run Code Online (Sandbox Code Playgroud)

kne*_*epp 5

正如评论中已经提到的:做内联是疯狂的,你最好使用一个单独的函数/ .m文件.

这将是

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

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

function out = R2TComputation(DL1, DL2, minLim, maxLim, C1)
...%Compute whatever R2T would compute.
Run Code Online (Sandbox Code Playgroud)

要获得与原始匿名函数相同的界面,您只需创建即可

R2T = @(DL1, DL2) R2TComputation(DL1, DL2, minLim, maxLim, C1)
Run Code Online (Sandbox Code Playgroud)

它将捕获当前值minLim,maxLimC1在您创建此句柄时R2T.


另一种选择是使用嵌套函数而不是外部函数.它可以访问父函数的变量,但仍然可以使用if,else以及您需要的所有其他基本工具.唯一的缺点:它不是要从其他文件中访问.

... % Main function stuff
     function out = R2T(DL1, DL2)
         if ...
            out = ...
         ...
     end
... % Use R2T ...
Run Code Online (Sandbox Code Playgroud)

然而,为了自由射击自己,这里是一个内联版本if-else,我是在Loren的博客文章的精神中写,我不建议使用,因为使用单个表达式几乎没有任何好处相应的if- else陈述.

ifelse = @(cond, varargin) varargin{1+~cond}(); %Only for the insane
Run Code Online (Sandbox Code Playgroud)

如果你想让它进行延迟评估,你需要传递一个零参数的匿名函数,ifelse然后进行评估(这就是最后两个括号()ifelse的内容):

ifelse(true, 42, @()disp('OMG! WTF! THIS IS CRAZY!!111'))
Run Code Online (Sandbox Code Playgroud)

如果您只是将函数调用disp作为参数编写为ifelsewithout @(),则在我们访问之前将调用该函数ifelse.这是因为MATLAB(与大多数其他语言一样)首先计算函数的返回值,然后将其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;
Run Code Online (Sandbox Code Playgroud)