2

I see equivalent operations being done several times in my Matlab code, and I think, there must be a way to condense these and do it all once. In this case it was checking whether some variables were less than a certain epsilon:

if abs(beta) < tol
    beta = 0;
end
if abs(alpha) < tol
    alpha = 0;
end
if abs(gamma) < tol
    gamma = 0;
end

I tried combining these into a few lines that deal with the whole boodle,

overTol = [alpha,beta,gamma] >= tol;
[alpha,beta,gamma] = [alpha,beta,gamma] .* overTol;

If this was Python, where a list is a list is a list, that would be fine, but in Matlab, an operation on the right produces one variable on the left except in special situations.

After reading Define multiple variables at the same time in MATLAB? and Declare & initialize variables in one line in MATLAB without using an array or vector, I tried using deal, but [alpha,beta,gamma] = deal([alpha,beta,gamma] .* overTol); does not distribute the terms of the vector given to the deal function between the terms in the vector on the left, but instead gives a copy of the whole vector to each of the terms.

The last thing I want to do is set the right equal to a unique vector and then set alpha, beta, and gamma equal to the terms of that vector, one by one. That's just as ugly as what I started with.

Is there an elegant solution to this?

Post169
  • 668
  • 8
  • 26
  • 2
    With `deal` you still need to explicitly repeat the operation on the right: `[alpha, beta] = deal(alpha.*(abs(alpha)>=tol), beta.*(abs(beta)>=tol));` You could define an anonymous function `f = @(x) x.*abs(x)>=x;` and then `alpha = f(alpha); beta = f(beta); gamma= f(gamma)`. To produce the most compact code you should collect the three variables into a single one (N-dim array or cell array). Would that be an option? – Luis Mendo Nov 22 '22 at 18:46
  • 2
    MATLAB does not split arrays into variables easily because MATLAB is designed to work with the arrays themselves, not with scalar values. There are tricks you could use, but I wouldn’t want to maintain code that uses these tricks. What is the problem with repeating a statement three times for three different variables? – Cris Luengo Nov 22 '22 at 19:00
  • 1
    See also https://stackoverflow.com/q/2337126/7328782 – Cris Luengo Nov 22 '22 at 19:25
  • @LuisMendo Yes, I ended up putting ```alpha```, ```beta```, and ```gamma``` together into ```alpha_beta_gamma```, which worked because they ended up serving as components of the same array and nothing else. But what if I need to run each through several operations? Well, in that case I think @CrisLuengo's reference might help a whole lot, though I'm not sure at the moment if it goes the whole way. – Post169 Nov 23 '22 at 00:00
  • 1
    @Post169 What operations do you anticipate running your values through? In this case you've reduced your 3 `if` clauses to `alpha_beta_gamma(abs(alpha_beta_gamma) – beaker Nov 23 '22 at 00:09
  • 2
    @Post169 Once you have the three variables in an array, that should make it easier to apply _any_ common operation to all of them... What operations are thinking of? Can you give a specific example? – Luis Mendo Nov 23 '22 at 01:15
  • The fact of the matter is, I don't need to do any further operations, but just set a row of a matrix equal to ```alpha_beta_gamma```. Thank you all, @LuisMendo, @CrisLuengo, and @beaker, for your comments! I hope this is helpful for others, even though I didn't accept an actual answer. – Post169 Nov 25 '22 at 22:47

2 Answers2

0

Ok, the comments explain why there are probably better approaches. I was curious if it could be done. It can, but maybe it shouldn't be.

% Initial values
a = 0.01; b = 0.1; c = 1; 
% Tolerance
tol = 0.05;
% Function - note returns a scalar cell
f = @(f) {f.*(abs(f)>tol)}
% arrayfun returns a cell of the new values
% cell2struct turns that into a struct array with a single field 'f'
% the trailing dot-reference extracts that field and builds a 
% comma-separated list which can be used to assign the new values
[a2,b2,c2] =  cell2struct(arrayfun(f, [a,b,c]),{'f'}).f
Edric
  • 23,676
  • 2
  • 38
  • 40
0

You could use cellfun to generate a cell array of results, and deal to redistribute it back into variables

f = @(x) x.*(abs(x)>tol);             % function which returns x, or x if abs(x)<tol
d = cellfun( f, {a,b,c}, 'uni', 0 );  % apply function to inputs a,b,c
[a,b,c] = deal(d{:});                 % deal results back to a,b,c

A more readable (and maintenance-friendly) method might be to just use functions and accept that it's going to use more lines. Line count is not the enemy...

a = applyTol(a);
b = applyTol(b);
c = applyTol(c);

function x = applyTol( x )
    tol = 0.5;
    if abs(x) < tol
        x = 0;
    end
end 
Wolfie
  • 27,562
  • 7
  • 28
  • 55