Logical short circuit inside the function knob

I have a function descriptor that works on 2d arrays of arbitrary size:

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

Here comes the upward breakdown of what he does:

  • fzero(@(x)fFitObj1(x)./fFitObj2(x)-DL1./DL2,[minLim maxLim]) - This bit searches for the zero of the function in question in the interval [minLim maxLim] , where fFitObj1 and fFitObj2 are service descriptors, previously available, C1 is some known constant and DL1, DL2 .
  • @(DL1,DL2)1/(fzero(...)) - a wrapper for fzero that allows you to provide DL1 and DL2 from the outside.
  • arrayfun(@(DL1,DL2)...,DL1,DL2) is another shell that allows fzero correctly control element by element when DL1, DL2 provided as a matrix.
  • R2T = @(DL1,DL2) arrayfun(...) - C1; - another shell that allows you to provide DL1, DL2 from the outside.

My problem is that sometimes the matrices DL1, DL2 can contain NaN values, in which case fzero returns the following error:

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

That's why I naturally thought about the available short-circuit operations, so I tried to include any(isnan([DL1,DL2])) in this so that fzero would not even be evaluated if its inputs are NaN - but that's all I I try (for example, a user ternary operator), it looks like fzero and code errors.

Desired result: I would like to implement a lazy evaluation of fzero only if the inputs are valid (in this case, not NaN ), and return NaN otherwise, as shown in Change below.

Related Resources:


Edit:

Here is a piece of code that illustrates the problem (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( ..... %} 

Desired result if B:

  ans = -99 -99 -99 NaN 
+6
source share
1 answer

As mentioned in the comments : The execution of this line of insanity , and you are much better off using a separate function / .m file.

It will be

  • Faster
  • Easier to read
  • Easier to write
  • Easier to debug

You can do this, for example, similarly to this:

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

To get the same interface as the original anonymous function, you can simply create

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

which will record the current values โ€‹โ€‹of minLim , maxLim and C1 during the creation of this R2T descriptor.


Another option would be to use a nested function instead of an external one. He will have access to the parent variables of the function, but can still use if , else and all the other basic tools you need. The only drawback: it is not intended to be accessed from other files.

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

However, for the sake of freedom to shoot in the foot, here is the built-in version of if-else , which I wrote in the spirit of Loren and I do not recommend using it , since it is hardly possible to use one expression instead of the corresponding if - else .

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

If you want a lazy rating , you need to pass an anonymous function with null parameters, which ifelse will then evaluate (which is for the last two parentheses () in ifelse ):

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

If you simply wrote a call to the disp function as an ifelse argument without @() , the function is called before we even get access to ifelse . This is because MATLAB (like most other languages) first calculates the return value of the function, which is then passed to ifelse as a parameter.

In your case, the resulting code will look like this:

 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; 
+5
source

Source: https://habr.com/ru/post/983191/


All Articles