三元运算符广泛存在于其他语言中,比如:

python:

val = trueValue if expr else falseValue

javascript:

const val = expr ? trueValue : falseValue

c、c++:

const char *val = expr ? "trueValue" : "falseValue";

然而,被广泛支持的三目运算符在golang中却是不存在的!如果我们写出类似下面的代码:

val := expr ? "trueValue" : "falseValue"

那么编译器就该抱怨了:invalid character U+003F '?'。意思是golang中不存在?这个运算符,编译器不认识而且非字母数字下划线也不能用做变量名,自然也就当作是非法字符了。

然而这是为什么呢,其实官方给出了解释,这里简单引用一下:

The reason ?: is absent from Go is that the language's designers had seen the operation used too often to create impenetrably complex expressions. The if-else form, although longer, is unquestionably clearer. A language needs only one conditional control flow construct.

golang中不存在?:运算符的原因是因为语言设计者已经预见到三元运算符经常被用来构建一些极其复杂的表达式。虽然使用if进行替代会让代码显得更长,但这毫无疑问可读性更强。一个语言只需要有一种条件判断结构就足够了。

毫无疑问,这是在golang“大道至简”的指导思想下的产物。

这段话其实没问题,因为某些三元运算符的使用场景确实会降低代码的可读性:

const status = (type===1?(agagin===1?'再售':'已售'):'未售')

const word = (res.distance === 0) ? 'a'
: (res.distance === 1 && res.difference > 3) ? 'b'
: (res.distance === 2 && res.difference > 5 && String(res.key).length > 5) ? 'c'
: 'd';

乍一看确实很复杂,至少第二个表达式不花个20秒细看可能没法理清控制流程(想象一下当缩进错位或是完全没有缩进的时候)。

如果把它们直接转化成if语句是这样的:

let status = ''
if (type === 1) {
if (again === 1) {
status = '再售'
} else {
status = '已售'
}
} else {
status = '未售'
} let word = ''
if (res.distance === 0) {
word = 'a'
} else {
if (res.distance === 1 && res.difference > 3) {
word = 'b'
} else {
if (res.distance === 2 && res.difference > 5 && String(res.key).length > 5) {
word = 'c'
} else {
word = 'd'
}
}
}

看起来并没有多少的改善,特别是例2,三层嵌套,不管是谁review到这段代码难免不会抱怨你几句。

然而事实上这些代码是可以简化的,考虑到三元运算符总是会给变量一个值,因此最后的else其实可以看作是变量的默认值,于是代码可以这么写:

let status = '未售'
if (type === 1) {
if (again === 1) {
status = '再售'
} else {
status = '已售'
}
} let word = 'd'
if (res.distance === 0) {
word = 'a'
} else {
if (res.distance === 1 && res.difference > 3) {
word = 'b'
} else {
if (res.distance === 2 && res.difference > 5 && String(res.key).length > 5) {
word = 'c'
}
}
}

其次,对于例2,显然可以使用else if来清除嵌套结构:

let word = 'd'
if (res.distance === 0) {
word = 'a'
} else if (res.distance === 1 && res.difference > 3) {
word = 'b'
} else if (res.distance === 2 && res.difference > 5 && String(res.key).length > 5) {
word = 'c'
}

现在再来看,显然使用if语句的版本的可读性更高,逻辑也更清晰(通过去除嵌套)。

然而事实也不尽然。除了用三元运算符表达流程控制之外,事实上更常见更广泛的一个应用是如下这样的表达式:

const val = expr ? trueValue : falseValue

const func = (age) => age > 18 ? '成年人' : '未成年人'

类似上述通过一个简短的条件表达式来确定变量的值,在开发中的出现频率是相当高的。这时三元运算符的意图更清晰,可读性也较if语句更高,特别是配合匿名函数(lambda表达式)使用可以极大简化我们的代码。

对此python的解决之道是之支持上述的简化版三元表达式,同时表达式不支持嵌套,达到了扬长避短的目的。不过代价是编译器的相关实现会复杂化。

而对于golang来说一个简单的能只通过单遍扫描即可完成ast构建的编译器是其保持急速的构建速度的秘诀之一,为了这样简单的功能增加编译器实现的复杂度是不可接受的。同时由于golang“大道至简”的哲学,能用现有语法结构解决的问题,自然不会再添加新的语法。

不过还是有办法的,虽然不推荐:

func If(cond bool, a, b interface{}) {
if cond {
return a
} return b
} age := 20
val := If(age > 18, "成年人", "未成年人").(string)

不推荐这么做是有几个原因:

  1. 使用接口导致性能下降
  2. 需要强制的类型断言
  3. 不管三元表达式还是if语句,对于不会到达的分支是不会计算的,也就是惰性计算;而给函数传递参数时每一个表达式都会被计算

最后总结一下:

三元运算符的优点:

  • 对于简短的表达式使用三元运算符表意更清晰,特别是在习惯了线性阅读三元运算符表达式之后
  • 不需要中间状态(例如第一个例子中的let变量可以替换为const,代码更健壮),心智负担更低
  • 没有中间状态也就意味着更少或完全没有副作用,代码更易跟踪和维护

但三元运算符也有明显的缺点:

  • 对于复杂逻辑代码可读性较差(例如第一个例子中的status,需要在trueValue的位置进行进一步的条件判断时)
  • 容易被滥用,很多人将其用于替代if语句或是简化复杂的if嵌套,这会导致上一条中所描述的结果
  • 条件分支只能为表达式,不支持多条语句

所以这是一个见仁见智的问题,总之只能入乡随俗了。

参考

https://juejin.im/post/6844903561759850510

https://www.it-swarm.dev/zh/javascript/替代js中的嵌套三元运算符/1055944752/

https://golang.org/doc/faq#Does_Go_have_a_ternary_form

为什么golang中不存在三元运算符的更多相关文章

  1. C#中唯一的三元运算符

    条件运算符?:接受三个操作数,是C#中唯一的三元运算符 ; ? : ; //转换成if选择结果如下 ) { j = ; } else { j = ; } 需要根据还可以嵌套三元运算符 ; ) ? : ...

  2. [转]JSP中EL表达式三元运算符的使用

    原文地址:http://www.guance.com/469.html Java中的三元运算符为:条件?条件为true值:条件为false的值EL也有一样的运算符,用EL的三元运算符有时可以代替c:c ...

  3. 条件运算符?:接受三个操作数,是C#中唯一的三元运算符(转)

    int i = 10; int j = i == 10 ? 1 : 2; //转换成if选择结果如下 if (i == 10) { j = 1; } else { j = 2; } 需要根据还可以嵌套 ...

  4. 在JavaScript中,利用三元运算符生成当前日期yyyy-MM-dd

    <script type="text/javascript"> //得到当前时间yyyy-MM-dd var myDate = new Date(); var nowD ...

  5. PHP运算符-算术运算符、三元运算符、逻辑运算符

    运算符是用来对变量.常量或数据进行计算的符号,它对一个值或一组值执行一个指定的操作.PHP的运算符包括算术运算符.字符串运算符.赋值运算符.位运算符.逻辑运算符.比较运算符.递增或递减运算符.错误控制 ...

  6. PHP基础语法之 三元运算符和其它运算符

    三元运算符和其它运算符 此外还有一些特殊的运算符和符号,我们再来进行讲解.可能以后我们需要用到.直线电机选型 符号 说明 $x? 真代码段:假代码段 判断是否为真假 ? 真情况 : 假情况; ``(反 ...

  7. PHP中的运算符---位运算符、递增递减运算符、三元运算符、字符串运算符、数组运算符、类型运算符、错误控制运算符

    1.位运算符 位运算符用来对整型数的指定位进行置位,如果被操作数是字符串,则对该字符串的ASCII码值进行操作. 运算类型 运算符 举例 结果 按位与 & $a & $b 将$a 与 ...

  8. Python中的三元运算符

    Python中的三元运算符 对于如下需求: if var1>1 : goal = "执行表达式1" else: goal = "执行表达式2" 1.在其他 ...

  9. php 中更简洁的三元运算符 ?:

    PHP 三元运算符是对参数赋值时候的一个简洁的主要用法. 一个主要的用法: PHP 三元运算符能够让你在一行代码中描述判定代码, 从而替换掉类似以下的代码: <?php if (isset($v ...

随机推荐

  1. 深入探究JVM之类加载与双亲委派机制

    @ 目录 前言 类的生命周期 加载 验证 准备 解析 初始化 案例一 案例二 案例三 案例四 类加载器 类加载器和双亲委派模型 破坏双亲委派模型 第一次 SPI Tomcat OSGI 总结 前言 前 ...

  2. 从入门到熟悉HTTPS的9个问题

    原文:bestswifter   https://juejin.im/post/58c5268a61ff4b005d99652a Q1: 什么是 HTTPS? BS: HTTPS 是安全的 HTTP ...

  3. Maven执行Reimport命令报错:See logs for details

    Idea版本:2018.1.3 maven版本:3.6.2 Idea配置了本地下载的maven之后,不管是直接执行maven的Reimport命令,还是使用idea的Reimport按钮,都会报这个错 ...

  4. syslog协议及rsyslog服务全解析

    背景:需求来自于一个客户想将服务器的日志转发到自己的日志服务器上,所以希望我们能提供这个转发的功能,同时还要满足syslog协议. 一.什么是syslog协议 1.介绍(略) 2.syslog标准协议 ...

  5. 用 Python 制作一个艺术签名小工具,给自己设计一个优雅的签名

    生活中有很多场景都需要我们签字(签名),如果是一些不重要的场景,我们的签名好坏基本无所谓了,但如果是一些比较重要的场景,如果我们的签名比较差的话,就有可能给别人留下不太好的印象了,俗话说字如其人嘛,本 ...

  6. 20行代码教你用python给证件照换底色

    1.图片来源 该图片来源于百度图片,如果侵权,请联系我删除!图片仅用于知识交流.本文只是为了告诉大家:python其实有很多黑科技(牛逼的库),我们既可以用python处理工作中的一些事儿,同时我们也 ...

  7. Javascript注意点

    Javascript注意点 在img标签中的src如果为相对路径, 但是在js获取的时候会转为全路径 候选框中, 在执行onclick之前, 会由于html的特征先设置checked属性 为a标签添加 ...

  8. Docker 快速搭建 MySQL 5.6 开发环境

    使用 Docker 快速搭建一个 MySQL 5.6 开发环境 步骤 获取镜像 docker pull mysql:5.6 启动容器,密码 123456,映射 3306 端口 docker run - ...

  9. Docker-Compose介绍,安装和使用

    Docker-Compose 介绍 有时候运行一个镜像需要大量的参数,可以通过Docker-Compose编写这些参数.而且Docker-Compose可以版主我们批量管理容器,这些信息值需要通过一个 ...

  10. MixNet:MixConv:Mixed Depthwise Convolution kernels