三元运算在 Octave 中有效吗?

Is Ternary operation valid in Octave?

问题是有效的三元运算,因为我无法在网上找到任何相关文档。而且我还发现在 MATLAB 中不可能使用三元,所以任何建议和答案将不胜感激。

#三元运算的代码

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

#用 if 和 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

输出:

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
>>

任何语言中三元运算符的任何定义与 if 语句.

之间存在一些重要差异

主要是后者是一个语句,而三元运算符根据定义是一个表达式。换句话说,你期望它给return一个值。 octave/matlab 中的 if 块不会“return”一个值(即你不能做 a = if ... endif)。如果你需要一些东西'returned',你需要在循环内将它赋给一个外部变量,然后在循环外使用那个变量。

第二件大事,三元运算符应该可链接。也就是说,您应该能够做到“如果这样,那么那样,否则如果那样,那么其他事情”,这样在一长串比较的末尾,您 return 一个值。


现在octave和matlab都没有这种算子。但是在八度音程中,很容易模拟一个,例如使用单元格:

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

如果你真的想要,你可以把那种东西变成一个很好的匿名函数,return它是第一个 mask/test 为真的表达式:

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

其中 'fallback' 只需通过在 'fallback' 值之前明确评估为 'true' 的测试来实现。

注意:这种 'chaining' 匿名函数中的两个或多个索引操作的样式仅适用于八度音阶,不幸的是 matlab 不允许链式操作,例如a(1)(2)。没有什么能阻止你创建一个等价的 'proper' 外部函数,你可以在第二次索引之前将中间结果保存到一个临时变量中;因此,从概念上讲,该策略也适用于 matlab。


或者,您可以使用稍微不同的语法创建更简单的 'ternary operator'(好吧,函数),其中您传递一个 'tests/masks' 数组、'expressions' 和 'fallback' 细胞分开。例如

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

最后,还有ifelse函数,它在本质上是相似的,即ifelse(test, expr_if_true, expr_if_false),但这并不是真正的三元operator/function,因为它是不可链接的;根据 'true/false mask' 在两个备选方案之间进行选择更有用,例如

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

你可以用两个短路&&||运算符来模拟三元if运算符。
考虑这个 if/else 语句:

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

t 定义为 t = @(x) 1; 并将三元 if 运算符定义为:

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

实现三元 if 运算符的一个重点是表达式 expr1expr2 的计算。如果 conditiontrue,则不能计算 expr2,如果 conditionfalse,则不能计算 expr1。在像 ifelse/merge 这样的函数中,两个表达式在传递给函数之前都会被求值,因此 ifelse 不应被视为真正的三元 if 运算符。

有一些基于 MATLAB 的实现 and 用于创建递归匿名函数。诀窍是表达式被 lambda 包裹起来以防止对其求值。
考虑以下计算第 n 个斐波那契数的函数:

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

当前提议的技术可用于实现它(在 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;

在递归匿名函数以外的情况下你可以使用这个:

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

然而,这些技巧可能不会比 if/else 语句更好。

考虑实现三元条件运算符函数

不知道 matlab 版本是否可以处理字符串和数组,因为这个可以

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;

一些示例调用

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