'Is Ternary operation valid in Octave?

Question is ternary operation valid as i am not able to find any document related to it online. And i also find out that ternary is not possible in MATLAB so any suggestion and answers will be appreciated here.

#Code with ternary operation

taxes = (income > 50000)*(((income-50000) * 0.20)+(0.10*50000)) + (~(income > 50000))*0.10 *50000
#Condition True and this computation False then this computation

#Code with if and else

if (income) > 50000
#income = income - 50000
#Taxed at 10% (i.e 0.10) for $ 50000
#taxes = 50000 * 0.10
#Rest income will be Taxed at 20% (i.e 0.20)
taxes = 50000 * 0.10 + ((income-50000) * 0.20)
else
#Taxed at 10% (i.e 0.10)
taxes = income * 0.10
endif

Output:

GNU Octave, version 6.1.0
Copyright (C) 2020 The Octave Project Developers.
This is free software; see the source code for copying conditions.
There is ABSOLUTELY NO WARRANTY; not even for MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  For details, type 'warranty'.

Octave was configured for "x86_64-w64-mingw32".

Additional information about Octave is available at https://www.octave.org.

Please contribute if you find this software useful.
For more information, visit https://www.octave.org/get-involved.html

Read https://www.octave.org/bugs.html to learn how to submit bug reports.
For information about changes from previous versions, type 'news'.

>> income = 60000;
>> compute_taxes
taxes = 7000
>> income = 50000;
>> compute_taxes
taxes = 5000
>>


Solution 1:[1]

There are a few important differences between any definition of a ternary operator in any language, and an if statement.

The main one is that the latter is a statement, whereas a ternary operator is by definition an expression. In other words, you expect it to return a value. An if block in octave/matlab does not "return" a value (i.e. you can't do a = if ... endif). If you need something 'returned', you need to assign it to an external variable inside the loop, and then use that variable outside the loop.

The second big thing, is that a ternary operator should be chainable. Ie you should be able to do "if this, then that, otherwise if that then that other thing", such that at the end of a long chain of comparisons you return a single value.


Now, octave and matlab do not have this kind of operator. But in octave, it's easy enough to simulate one, e.g. with cells:

M1 = false; M2 = false; M3 = true;
{'expression1', 'expression2', 'expression3', 'fallback'}([ M1, M2, M3, true ]){1}

If you really want to, you can make that kind of thing into a nice anonymous function, which returns the first expression for which its mask/test was true:

tern = @(varargin) reshape(varargin,2,[])(2,:)([reshape(varargin,2,[]){1,:}]){1}
tern( M1, 1, M2, 2, M3, 3, true, 4 )   % ans = 3

where 'fallback' is achieved simply by having a test that evaluates to 'true' explicitly before the 'fallback' value.

Note: This style of 'chaining' two or more indexing operations inside an anonymous function only works in octave, as unfortunately matlab does not allow chained operations, e.g. a(1)(2). There is nothing stopping you from making an equivalent 'proper', external function though, where you save intermediate results to a temporary variable before indexing it a second time; therefore, conceptually, this strategy will also work in matlab.


Alternatively, you could make a simpler 'ternary operator' (well, function) using a slightly different syntax, where you pass a 'tests/masks' array, and 'expressions', and 'fallback' cells separately. E.g.

tern2 = @(M,E,F) [E, F]{find([M, true], 1)}
tern2( [M1, M2, M3], {1, 2, 3}, {4} )   % ans = 3

Finally, there is also the ifelse function, which is similar in spirit, i.e. ifelse(test, expr_if_true, expr_if_false), but this is not really a true ternary operator/function, since it is not chainable; it's more useful for choosing between two alternatives based on a 'true/false mask', e.g.

ifelse( [M1, M2, M3], {1, 2, 3}, {4,5,6} )
% ans = { 4, 5, 3 }

Solution 2:[2]

You can simulate ternary if operator with two short circuit && and || operators.
Consider this if/else statement:

if condition 
    val = expr1;
else
    val = expr2;
end

Define t as t = @(x) 1; and the ternary if operator as:

condition && t(val = expr1) || t(val = expr2);

An important point in implementing the ternary if operator is the evaluation of expressions expr1 and expr2. If condition is true then expr2 must not be evaluated and if condition is false then expr1 must not be evaluated. In the functions like ifelse/merge both expressions are evaluated before they are passed to the function so ifelse shouldn't be considered as a true ternary if operator.

There are some MATLAB based implementations here and here that are used to create recursive anonymous function. The trick is that the expressions are wrapped by a lambda to prevent their evaluation.
Consider the following function that computes the nth Fibonacci number:

function val = fib(f, n)
  if n <= 2
    val = 1;
  else
    val = f(f, n - 1) + f(f, n - 2));
end

The current proposed technique can be used to implement it as (tested on Octave 6.1.0):

fib = @(f, n, t = @(x) 1, tmp = n <= 2 && t(val = 1) || t(val = f(f, n - 1) + f(f, n - 2))) val;

In situations other than recursive anonymous function you can use this:

val = {condition && t(tmp = expr1) || t(tmp = expr2), tmp}{2};

However those tricks may not perform better than if/else statement.

Solution 3:[3]

Consider implementing a ternary conditional operator function

Don't know if the matlab version can handle strings, and arrays as this can

clear ternary
function tern = ternary( test, alternatives); 
  if length( alternatives(:,1)) == 1,, alternatives = alternatives(:); end;
  if test,, tern = alternatives(1,:);
  else,     tern = alternatives(2,:); end
return; end;

some sample calls

methodss =      ['Runge-Kutta';           'Adams-MLS']; 
adaptive_step = ['adaptive step control'; 'fixed step size'];
report_method = @(selectmethod) [char(    ternary    (selectmethod(1), methodss     )) ' ' ...
                                 char(    ternary    (selectmethod(2), adaptive_step))    ];
%
report_method( [true  false]),; % self-starting = true ;   adaptive step size = false
report_method( [false true ]),; % self-starting = false;   adaptive step size = true

% ans = Runge-Kutta fixed step size
% ans = Adams-MLS   adaptive step control

ternary(2 >  2, [123 456]) %  456
ternary(2 >= 2, [123 456]) %  123

ternary(2 >  2, [123 456; 789 999]) %  789   999
ternary(2 >= 2, [123 456; 789 999]) %  123   456

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3 Donovan Roisin