我个人是三元运算符的拥护者: () ?: ; 我确实意识到它有它的位置,但我遇到过许多完全反对使用它的程序员,有些程序员使用它过于频繁。
你对此有何感想?你见过什么有趣的代码使用它?
我个人是三元运算符的拥护者: () ?: ; 我确实意识到它有它的位置,但我遇到过许多完全反对使用它的程序员,有些程序员使用它过于频繁。
你对此有何感想?你见过什么有趣的代码使用它?
仅用于简单表达式:
int a = (b > 10) ? c : d;
不要链接或嵌套三元运算符,因为它难以阅读和混淆:
int a = b > 10 ? c < 20 ? 50 : 80 : e == 2 ? 4 : 8;
此外,在使用三元运算符时,请考虑以提高可读性的方式格式化代码:
int a = (b > 10) ? some_value
: another_value;
由于不能在每个子表达式上放置断点,因此调试会稍微困难一些。我很少使用它。
我喜欢它们,尤其是在类型安全的语言中。
我不明白这是怎么回事:
int count = (condition) ? 1 : 0;
比这更难:
int count;
if (condition)
{
count = 1;
}
else
{
count = 0;
}
我认为三元运算符使一切都比其他运算符更简单,更简洁。
链接我很好 - 嵌套,不是那么多。
我倾向于在 C 中更多地使用它们,因为它们是一个有价值的if语句,因此它减少了不必要的重复或变量:
x = (y < 100) ? "dog" :
(y < 150) ? "cat" :
(y < 300) ? "bar" : "baz";
而不是
if (y < 100) { x = "dog"; }
else if (y < 150) { x = "cat"; }
else if (y < 300) { x = "bar"; }
else { x = "baz"; }
在这样的作业中,我发现重构更少,更清晰。
另一方面,当我在 ruby 中工作时,我更有可能使用它,if...else...end
因为它也是一个表达式。
x = if (y < 100) then "dog"
elif (y < 150) then "cat"
elif (y < 300) then "bar"
else "baz"
end
(虽然,诚然,对于这么简单的事情,我可能只是使用三元运算符。)
三元 ?:
运算符只是过程if
构造的功能等价物。因此,只要您不使用嵌套?:
表达式,支持/反对任何操作的功能表示的参数都适用于此处。但是嵌套三元操作可能会导致代码完全混乱(读者练习:尝试编写一个解析器来处理嵌套三元条件,您将体会到它们的复杂性)。
但是在很多情况下,保守地使用?:
运算符会导致代码实际上比其他方式更容易阅读。例如:
int compareTo(Object object) {
if((isLessThan(object) && reverseOrder) || (isGreaterThan(object) && !reverseOrder)) {
return 1;
if((isLessThan(object) && !reverseOrder) || (isGreaterThan(object) && reverseOrder)) {
return -1;
else
return 0;
}
现在将其与此进行比较:
int compareTo(Object object) {
if(isLessThan(object))
return reverseOrder ? 1 : -1;
else(isGreaterThan(object))
return reverseOrder ? -1 : 1;
else
return 0;
}
由于代码更紧凑,语法噪音更少,并且通过明智地使用三元运算符(仅与reverseOrder属性有关),最终结果并不是特别简洁。
这是一个风格问题,真的;我倾向于遵循的潜意识规则是:
foo = (bar > baz) ? true : false
,但不是foo = (bar > baz && lotto && someArray.Contains(someValue)) ? true : false
<%= (foo) ? "Yes" : "No" %>
(foo) ? FooIsTrue(foo) : FooIsALie(foo)
)我喜欢它,因为它对于简单的赋值操作来说简洁而优雅。
像许多意见问题一样,答案不可避免地是:这取决于
对于类似的东西:
return x ? "Yes" : "No";
我认为这比:
if (x) {
return "Yes";
} else {
return "No";
}
现在如果你的条件表达式很复杂,那么三元运算不是一个好的选择。就像是:
x && y && z >= 10 && s.Length == 0 || !foo
不是三元运算符的好候选。
顺便说一句,如果您是 C 程序员,GCC 实际上有一个扩展,允许您排除三元的 if-true 部分,如下所示:
/* 'y' is a char * */
const char *x = y ? : "Not set";
将设置x
为y
假设y
不是NULL
。好东西。
在我看来,只有在需要表达式的情况下使用三元运算符才有意义。
在其他情况下,三元运算符似乎降低了清晰度。
我尽可能使用三元运算符,除非它使代码非常难以阅读,但这通常只是表明我的代码可以使用一些重构。
总是让我感到困惑,有些人如何认为三元运算符是一个“隐藏”功能或有些神秘。这是我开始用 C 编程时学到的第一件事,我认为它根本不会降低可读性。这是语言的自然组成部分。
我经常在我被限制在构造函数中工作的地方使用它——例如,新的 .NET 3.5 LINQ to XML 构造——当可选参数为空时定义默认值。
人为的例子:
var e = new XElement("Something",
param == null ? new XElement("Value", "Default")
: new XElement("Value", param.ToString())
);
或(感谢asterite)
var e = new XElement("Something",
new XElement("Value",
param == null ? "Default"
: param.ToString()
)
);
不管你是否使用三元运算符,确保你的代码可读是重要的。任何构造都可能变得不可读。
我同意 jmulder 的观点:它不应该用来代替 a if
,但它在 return 表达式或表达式中占有一席之地:
echo "Result: " + n + " meter" + (n != 1 ? "s" : "");
return a == null ? "null" : a;
前者只是一个例子,应该使用对复数更好的国际化和本地化支持!
如果您使用三元运算符进行简单的条件赋值,我认为这很好。我已经看到它(ab)用于控制程序流程而无需进行分配,我认为应该避免这种情况。在这些情况下使用 if 语句。
(当天的黑客攻击)
#define IF(x) x ?
#define ELSE :
然后你可以做 if-then-else 作为表达式:
int b = IF(condition1) res1
ELSE IF(condition2) res2
ELSE IF(conditions3) res3
ELSE res4;
我认为应该在需要时使用三元运算符。这显然是一个非常主观的选择,但我发现一个简单的表达式(特别是作为返回表达式)比一个完整的测试要清晰得多。C/C++ 中的示例:
return (a>0)?a:0;
相比:
if(a>0) return a;
else return 0;
您还遇到解决方案介于三元运算符和创建函数之间的情况。例如在 Python 中:
l = [ i if i > 0 else 0 for i in lst ]
替代方案是:
def cap(value):
if value > 0:
return value
return 0
l = [ cap(i) for i in lst ]
在 Python 中(例如)经常会看到这样的习语,这是非常必要的:
l = [ ((i>0 and [i]) or [0])[0] for i in lst ]
此行使用 Python 中逻辑运算符的属性:它们是惰性的,如果等于最终状态,则返回最后计算的值。
我见过这样的野兽(实际上更糟糕,因为它是 isValidDate 并且还检查了月份和日期,但我不会费心去记住整个事情):
isLeapYear =
((yyyy % 400) == 0)
? 1
: ((yyyy % 100) == 0)
? 0
: ((yyyy % 4) == 0)
? 1
: 0;
显然,一系列 if 语句会更好(尽管这个仍然比我曾经看到的宏版本更好)。
我不介意这样的小事:
reportedAge = (isFemale && (Age >= 21)) ? 21 + (Age - 21) / 3 : Age;
甚至有点棘手的事情,例如:
printf ("Deleted %d file%s\n", n, (n == 1) ? "" : "s");
我几乎从不使用三元运算符,因为每当我使用它时,它总是让我想得比以后尝试维护它时要多得多。
我喜欢避免冗长,但是当它使代码更容易理解时,我会选择冗长。
考虑:
String name = firstName;
if (middleName != null) {
name += " " + middleName;
}
name += " " + lastName;
现在,这有点冗长,但我发现它比以下内容更具可读性:
String name = firstName + (middleName == null ? "" : " " + middleName)
+ " " + lastName;
或者:
String name = firstName;
name += (middleName == null ? "" : " " + middleName);
name += " " + lastName;
它似乎只是将太多的信息压缩到太小的空间中,而没有弄清楚发生了什么。每次我看到使用三元运算符时,我总能找到一个似乎更容易阅读的替代方案……再说一次,这是一个非常主观的意见,所以如果你和你的同事发现三元运算符非常可读,那就去吧。
我喜欢在调试代码中使用运算符来打印错误值,这样我就不必一直查找它们。通常我这样做是为了调试打印,一旦我完成开发就不会保留。
int result = do_something();
if( result != 0 )
{
debug_printf("Error while doing something, code %x (%s)\n", result,
result == 7 ? "ERROR_YES" :
result == 8 ? "ERROR_NO" :
result == 9 ? "ERROR_FILE_NOT_FOUND" :
"Unknown");
}
我喜欢他们。不知道为什么,但是我用三元表达式的时候感觉很爽。
好吧,它的语法是可怕的。我发现函数式 if非常有用,它们通常使代码更具可读性。
我建议制作一个宏以使其更具可读性,但我相信有人会想出一个可怕的边缘情况(就像 C++ 一样)。
我对待三元运算符很像 GOTO。它们有自己的位置,但通常应该避免使用它们以使代码更易于理解。
我通常在这样的事情中使用它:
before:
if(isheader)
drawtext(x, y, WHITE, string);
else
drawtext(x, y, BLUE, string);
after:
drawtext(x, y, isheader == true ? WHITE : BLUE, string);
正如其他人指出的那样,它们适用于简短的简单条件。我特别喜欢它们的默认值(有点像||和或JavaScript 和 Python 中的用法),例如
int repCount = pRepCountIn ? *pRepCountIn : defaultRepCount;
另一个常见用途是在 C++ 中初始化引用。由于必须在同一语句中声明和初始化引用,因此不能使用if语句。
SomeType& ref = pInput ? *pInput : somethingElse;
我最近看到了三元运算符的变体(嗯,有点),它使标准的“()?:”变体似乎是清晰的典范:
var Result = [CaseIfFalse, CaseIfTrue][(boolean expression)]
或者,举一个更具体的例子:
var Name = ['Jane', 'John'][Gender == 'm'];
请注意,这是 JavaScript,因此在其他语言中可能无法实现这样的事情(谢天谢地)。
对于简单的 if 情况,我喜欢使用它。实际上,读取/编码例如作为函数或类似事物的参数要容易得多。另外,为了避免新行,我喜欢保留我所有的 if/else。
在我的书中嵌套它是一个很大的禁忌。
因此,继续,对于单个 if/else,我将使用三元运算符。对于其他情况,常规 if/else if/else(或 switch)。
我喜欢 Groovy 的三元运算符的特殊情况,称为 Elvis 运算符:?:
expr ?: default
如果不为空,则此代码评估为 expr,如果为空,则为默认值。从技术上讲,它并不是真正的三元运算符,但它肯定与它相关并且节省了大量时间/打字。
对于简单的任务,例如根据条件分配不同的值,它们很棒。不过,当有更长的表达式取决于条件时,我不会使用它们。
如果您和您的同事了解他们的工作并且他们不是在大量群体中创建的,我认为他们使代码不那么复杂并且更易于阅读,因为代码更少。
我认为三元运算符使代码更难理解的唯一一次是当你在一行中有超过三个或大厅时。大多数人不记得它们是基于正确的优先级,当你有一堆它们时,阅读代码就会变成一场噩梦。
正如许多答案所说,这取决于. 我发现如果在快速扫描代码中看不到三元比较,那么就不应该使用它。
作为一个附带问题,我可能还会注意到它的存在实际上有点反常,因为在 C 中,比较测试是一种陈述。在Icon中,if
构造(像大多数 Icon)实际上是一个表达式。因此,您可以执行以下操作:
x[if y > 5 then 5 else y] := "Y"
...我发现它比三元比较运算符更具可读性。:-)
最近有一个关于将?:
操作符添加到Icon的可能性的讨论,但有几个人正确地指出,由于工作方式,绝对没有必要if
。
这意味着如果您可以在 C(或任何其他具有三元运算符的语言)中做到这一点,那么实际上您根本不需要三元运算符。
不,它们很难阅读。If/Else 更容易阅读。
这是我的意见。您的里程可能会有所不同。
我最近制定的确定是否应该使用三元运算符的经验法则是:
并善待您的代码的读者。如果要嵌套三元运算符,请格式化代码以使嵌套明显。
三元运算符放下。如果格式正确,它们并不复杂。以paxdiablo中的闰年为例:
$isLeapYear =
(($year % 400) == 0)
? 1
: ((($year % 100) == 0)
? 0
: ((($year % 4) == 0)
? 1
: 0));
这可以写得更简洁,并且使用这种格式更具可读性:
//--------------Test expression-----Result
$isLeapYear = (($year % 400) == 0) ? 1 :
((($year % 100) == 0)? 0 :
((($year % 4) == 0) ? 1 :
0)); // Default result
我会说逻辑表达式中的条件数量使其更难阅读。这适用于 if 语句,也适用于三元运算符。在一个完美的世界里,应该有一个可以概括的理由来选择一个分支而不是其他分支。如果您的解释是“仅当这组状态发生时”,那么它实际上更像是一个“业务规则”。
然而,在现实世界中,我们不会仅仅为了遵循理想情况而添加中间步骤来将状态折叠成一个可表达的状态。我们已经对多个状态进行了推断,并且必须就如何处理它们做出决定。
我喜欢三元组,因为它可以用 if 语句做任何事情。
if( object.testSomeCondition()) {
System.exec( "format c:" );
}
else {
a++;
}
另一方面:
a += ( object.testSomeCondition() ? 0 : 1 );
清楚地表明目标是为a
. 当然,与此一致,可能不应该超过合理的副作用。
if
在我决定是否有时间在上游返工条件以便回答一个更简单的问题后,我会使用长或复杂的条件。但是当我使用 if 时,我仍然尝试进行并行处理,只是在不同的条件下。
if ( user.hasRepeatedlyPressedOKWithoutAnswer()
&& me.gettingTowardMyLunchtime( time )
) {
...
}
我的目标也是近乎单流处理。所以我经常尽量不做一个else
和一个if
只是偏离共同道路的一步。当您进行大量单流处理时,很难将错误隐藏在代码中,等待一个会跳出并破坏事情的条件。
正如我上面所说,如果你使用三元来设置一个东西,或者你想要测试少量的案例以便将它设置为一个值,那么我只是喜欢三元的可读性。
有一个警告-->没有复杂的真实从句
a = b == c ? ( c == d ? ( c == e ? f : g ) : h ) : i;
当然可以分解为:
a = b != c ? i
: c != d ? h
: c == e ? f
: g
;
它看起来像一个(压缩的)真值表。
请记住,还有更重要的可读性因素。其中一个是块长度,另一个是缩进级别。在三元组中做简单的事情并不会推动越来越多的缩进级别。
用它来:
访问对象(数组)属性:
var status = statuses[error == null ? 'working' : 'stopped'];
返回语句:
function getFullName(){
return this.isMale() ? "Mr. " : "Ms. " + this.name;
}
初始化变量:
var formMethod = DEBUG_FLAG == true ? "GET" : "POST";
验证参数:
function(object){
var prop1 = typeof object.property == 'undefined'
? "default prop"
: object.property;
//...
}
代码示例在JavaScript中。
有趣的轶事:我已经看到优化器对三元运算符的权重比等效的if更不“重” 。我在 Microsoft 编译器中注意到了这一点,但它可能会更普遍。
特别是像这样的函数会内联:
int getSomething()
{
return m_t ? m_t->v : 0;
}
但这不会:
int getSomething()
{
if( m_t )
return m_t->v;
return 0;
}
我很喜欢。当我使用它时,我把它写成if-then-else:条件、真动作和假动作各一行。这样,我可以轻松地嵌套它们。
例子:
x = (a == b ? (sqrt(a) - 2) : (a*a + b*b) ); x = (a == b ? (sqrt(a) - 2) : (a*a + b*b) ); x = (a == b ? (c > d ? (sqrt(a) - 2) : (c + cos(d)) ) : (a*a + b*b) );
对我来说,这相当容易阅读。它还可以轻松添加子案例或更改现有案例。
我使用并推荐三元组以避免在逻辑微不足道的情况下出现代码行。
int i;
if( piVal ) {
i = *piVal;
} else {
i = *piDefVal;
}
在上述情况下,我会选择三元,因为它的噪音较小:
int i = ( piVal ) ? *piVal : *piDefVal;
同样,条件返回值是很好的候选者:
return ( piVal ) ? *piVal : *piDefVal;
我认为紧凑性可以提高可读性,从而有助于提高代码质量。
但可读性始终取决于代码的受众。
读者必须能够在a ? b : c
不费任何脑力的情况下理解该模式。如果您无法确定这一点,请选择长版本。
如果您的三元运算符最终占据整个屏幕宽度,那么我不会使用它。我只检查一个简单的条件并返回单个值:
int x = something == somethingElse ? 0 : -1;
我们实际上在生产中有一些像这样令人讨厌的代码......不好:
int x = something == (someValue == someOtherVal ? string.Empty : "Blah blah") ? (a == b ? 1 : 2 ): (c == d ? 3 : 4);
三元运算符对于简洁地生成逗号分隔列表非常有用。这是一个 Java 示例:
int[] iArr = {1, 2, 3};
StringBuilder sb = new StringBuilder();
for (int i = 0; i < iArr.length; i++) {
sb.append(i == 0 ? iArr[i] : ", " + iArr[i]);
}
System.out.println(sb.toString());
它产生:“1、2、3”
否则,最后一个逗号的特殊大小写会变得烦人。
如果您正在尝试减少代码中的行数或正在重构代码,那么就去做吧。
如果您关心下一个程序员必须多花 0.1 毫秒才能理解表达式,那么无论如何都要去做。
不,三元运算符不会增加复杂性。不幸的是,一些开发人员过于倾向于命令式编程风格,以至于他们拒绝(或不会学习)其他任何东西。我不相信,例如:
int c = a < b ? a : b;
比等价物“更复杂”(但更冗长):
int c;
if (a < b) {
c = a;
} else {
c = b;
}
或者更尴尬(我见过):
int c = a;
if (!a < b) {
c = b;
}
也就是说,请根据具体情况仔细查看您的替代方案。假设一个受过适当教育的开发人员,询问哪个最简洁地表达了您的代码的意图并使用那个。
我曾经在“三元运算符使一行不可读”阵营中,但在过去几年中,我逐渐喜欢适度使用它们。如果团队中的每个人都了解正在发生的事情,单行三元运算符可以提高可读性。这是一种简洁的方法,无需为了花括号而使用大量花括号。
我不喜欢它们的两种情况:如果它们超出 120 列标记太远,或者它们是否嵌入到其他三元运算符中。如果您不能在三元运算符中快速、轻松且可读地表达您正在做的事情。然后使用 if/else 等效项。
这取决于 :)
它们在处理可能为空的引用时很有用(顺便说一句:Java 确实需要一种方法来轻松比较两个可能为空的字符串)。
当您在一个表达式中嵌套许多三元运算符时,问题就开始了。
不(除非它们被滥用)。当表达式是更大表达式的一部分时,三元运算符的使用通常更清晰。
我认为这实际上取决于它们所使用的上下文。
这样的事情将是一种非常令人困惑的,尽管有效的使用它们的方式:
__CRT_INLINE int __cdecl getchar (void)
{
return (--stdin->_cnt >= 0)
? (int) (unsigned char) *stdin->_ptr++
: _filbuf (stdin);
}
然而,这:
c = a > b ? a : b;
完全合理。
我个人认为应该在减少过于冗长的 IF 语句时使用它们。问题是人们要么对它们感到害怕,要么非常喜欢它们,以至于几乎完全使用它们而不是 IF 语句。
字符串 someSay = bCanReadThis ?“否”:“是”;
在小剂量下,它们可以减少行数并使代码更具可读性;特别是如果结果类似于根据计算结果将 char 字符串设置为“是”或“否”。
例子:
char* c = NULL;
if(x) {
c = "true";
}else {
c = "false";
}
和....相比:
char* c = x ? "Yes" : "No";
像这样的简单测试中唯一可能出现的错误是分配了不正确的值,但由于条件通常很简单,因此程序员不太可能出错。让你的程序打印出错误的输出并不是世界末日,在所有代码审查、基准测试和生产测试阶段都应该抓住这一点。
我将反驳我自己的论点,现在使用代码覆盖率指标来帮助了解您的测试用例有多好变得更加困难。在第一个示例中,您可以测试两条分配线的覆盖率;如果没有涵盖,那么您的测试不会执行所有可能的代码流。
在第二个示例中,无论 X 的值如何,该行都将显示为正在执行,因此您不能确定您已经测试了备用路径(YMMV取决于您的覆盖工具的能力)。
随着测试复杂性的增加,这更重要。
似乎没有人提到使用三元运算符的原因之一是,至少在像D这样支持类型推断的语言中,它允许类型推断适用于非常复杂的模板类型。
auto myVariable = fun();
// typeof(myVariable) == Foo!(Bar, Baz, Waldo!(Stuff, OtherStuff)).
// Now I want to declare a variable and assign a value depending on some
// conditional to it.
auto myOtherVariable = (someCondition) ? fun() : gun();
// If I didn't use the ternary I'd have to do:
Foo!(Bar, Baz, Waldo!(Stuff, OtherStuff)) myLastVariable; // Ugly.
if(someCondition) {
myLastVariable = fun();
} else {
myLastVariable = gun():
}
在某些情况下我喜欢操作符,但我认为有些人倾向于过度使用它并且它会使代码更难阅读。
我最近在我正在修改的一些开源代码中偶然发现了这一行。
在哪里
(active == null ? true :
((bool)active ? p.active : !p.active)) &&...
代替
where ( active == null || p.active == active) &&...
我想知道在这种情况下,三元使用是否会增加LINQ语句的额外开销。
我同意这里许多海报的观点。三元运算符是完全有效的,只要它使用正确并且不会引入歧义(公平地说,您可以说任何运算符/构造)。
我经常在嵌入式代码中使用三元运算符来阐明我的代码在做什么。采取以下(为清楚起见,过度简化)代码示例:
片段1:
int direction = read_or_write(io_command);
// Send an I/O
io_command.size = (direction==WRITE) ? (32 * 1024) : (128 * 1024);
io_command.data = &buffer;
dispatch_request(io_command);
片段 2:
int direction = read_or_write(io_command);
// Send an I/O
if (direction == WRITE) {
io_command.size = (32 * 1024);
io_command.data = &buffer;
dispatch_request(io_command);
} else {
io_command.size = (128 * 1024);
io_command.data = &buffer;
dispatch_request(io_command);
}
在这里,我正在发送一个输入或输出请求。无论请求是读还是写,过程都是一样的,只是默认的 I/O 大小发生了变化。在第一个示例中,我使用三元运算符来表明过程是相同的,并且size
字段根据 I/O 方向获得不同的值。在第二个示例中,这两种情况的算法是否相同并不清楚(尤其是当代码长于三行时)。第二个例子更难保持公共代码同步。在这里,三元运算符在表达代码的大部分并行性质方面做得更好。
三元运算符还有另一个优势(尽管这通常只是嵌入式软件的问题)。一些编译器只有在代码没有“嵌套”超过一定深度时才能执行某些优化(意味着在函数内部,每次输入 if、循环或 switch 语句时嵌套深度增加 1,当你离开它)。有时,使用三元运算符可以最大限度地减少需要在条件中的代码量(有时到编译器可以优化掉条件的程度)并且可以减少代码的嵌套深度。在某些情况下,我能够使用三元运算符(如上面的示例)重新构造一些逻辑,并减少函数的嵌套深度,使编译器可以对其执行额外的优化步骤。
使代码更小并不总是意味着更容易解析。它因语言而异。
例如,在PHP中,鼓励使用空格和换行符,因为 PHP 的词法分析器首先将代码分解为以换行符开头的位,然后是空格。所以我没有看到性能问题,除非使用较少的空格。
坏的:
($var)?1:0;
好的:
($var) ? 1 : 0;
这似乎不是一个大问题,但是在 PHP 中对代码进行词法分析时,空格是必不可少的。另外,这样读起来也更好一些。
如果没有三元运算符,谁会赢得混淆代码竞赛?!
我个人喜欢在适当的时候使用它,但我认为我永远不会嵌套它。它非常有用,但它有一些缺点,因为它使代码更难阅读,并且在其他一些语言的其他操作中使用(例如Groovy的 null-check)。
我是它的忠实粉丝......在适当的时候。
像这样的东西很棒,而且,就个人而言,我觉得阅读/理解并不难:
$y = ($x == "a" ? "apple"
: ($x == "b" ? "banana"
: ($x == "c" ? "carrot"
: "default")));
不过,我知道这可能会让很多人感到畏缩。
在 PHP 中使用它时要记住的一件事是它如何与返回引用的函数一起工作。
class Foo {
var $bar;
function Foo() {
$this->bar = "original value";
}
function &tern() {
return true ? $this->bar : false;
}
function ¬Tern() {
if (true) return $this->bar;
else return false;
}
}
$f = new Foo();
$b =& $f->notTern();
$b = "changed";
echo $f->bar; // "changed"
$f2 = new Foo();
$b2 =& $f->tern();
$b2 = "changed";
echo $f2->bar; // "original value"