目录
如何理解Swift "case 10 ... 20 = a"这种条件表达式?
如何反向验证某个feature在特定标准才开启?
以"C++17引入了switch用初始化表达式"为例,
switch (int x = value; x) {
case 1 ... 10:
}
g++ -std=c++14 demo.cpp
-> 警告如下:init-statement in selection statements only available with ‘-std=c++17’ or ‘-std=gnu++17
g++ -std=c++17 demo.cpp
-> 无任何警告。
case语句fallthrough
大家知道,C/ObjC/C++的switch/case语句中,每个case跟随的语句,如果没有break语句会默认fallthrough. 这产生了一个经常发生bug的地方,不小心遗漏break导致意外的fallthrough.
- Swift改善了这一点,case语句后面默认隐式带有"break", 如需要fallthrough,需特别写fallthrough语句。它的设计和C语言完全相反,首先默认带break,避免不小心的bug,特别需要fallthrough就指定。
- Go语言同样以安全优先,默认带break,如需fallthrough需特别写fallthrough.
- Pascal语言每个case语句默认会结束上一个case语句。
case默认不break有什么优势?
- 对于C语言而言,case标号其实就是普通的label,汇编语言理解为可以跳转的label. 当有些解析程序对于多个case用同样解析方法,这种设计很方便简洁。
如何理解Swift "case let xxx:"?
一样,我们可以找到Swift switch语法定义:Switch Statements | swift.org
case-label → attributes? case case-item-list :
case-item-list → pattern where-clause? | pattern where-clause? ,
case-item-list
其实,let xxx是一种idenfifier pattern (Identifier Patterns | swift.org), 如下:
pattern → wildcard-pattern type-annotation?
pattern → identifier-pattern type-annotation?
pattern → value-binding-pattern
pattern → tuple-pattern type-annotation?
pattern → enum-case-pattern
pattern → optional-pattern
pattern → type-casting-pattern
pattern → expression-pattern
如何理解Swift "case 10 ... 20 = a"这种条件表达式?
Swift提供了Case Pattern Initializer用来初始化一个变量,如下可以判断a是否在10~20之间。
if case 10 ... 20 = a {
print("a is in 10 ~ 20")
}
可参考Swift语言标准条件语句语法:
condition → expression | availability-condition | case-condition | optional-binding-condition
case-condition → case pattern initializer
Refer to: Statements | Documentation (swift.org)
case语句可以动态筛选?
传统意义上,C/ObjC/C++不支持case匹配的动态筛选,只有静态且具体值对应匹配。
- Swift提供了case <pattern> 可以动态筛选条件,比较灵活强大。技术上并不复杂,只是将之前的单一静态条件判断变成<pattern>条件判断语句。
- C++17允许switch初始化表达式(switch 语句 (C++) | Microsoft Learn),但case依然局限于具体值范围,不能加入变量条件。
GNU扩展对C/C++都允许case范围匹配,例如如下case 1 ... 10,但MSVC不支持。
switch (int x = value; x) { // Only C++17 works
case 1 ... 10: // GNU C/C++ both works
}
- 此处需特别注意,case 1...10会编译出错,"..."前后必须有空格(Case Ranges | GCC).
- 可能会发现用gcc -std=c89或g++ -std=c++98都可以编译过如上case 1 ... 10,不要惊慌,可能你使用的GCC版本default就启用case范围匹配,哪怕指定用标准C语言或者C++也不能阻挡GCC对范围匹配的支持。 - C# 7.0 引入的模式匹配(Pattern Matching)允许开发者在
switch
语句中使用更复杂的条件来匹配不同的情况,不止基于常量值,还能基于数据类型、属性、条件关系等进行分支,因为太灵活了,此处不举例了。 - Java 12对switch表达式进行优化,允许多值并带有条件判断。
- JavaScript case允许任意表达式,且case表达式是运行时求值而非C/C++编译期求值,所以JS的case表达式性能会低很多。
- Kotlin 用when语句支持传统语言的switch/case,也支持范围匹配和表达式作为条件判断。
switch/case语句
多路分支选择switch/case是if/else if/else的好看版本,switch中default语句对应if的else语句。每个case对应汇编代码的label, 编译器插入多条jmp语句实现不同分支跳转.
条件类型
- C/C++条件类型必须是整型、字符或枚举,不能是字符串。
- C#除了C/C++支持的类型,还可以支持字符串形式,Java 7 switch条件加入了字符串类型。
- Python 3.10加入了match case语句支持多条件选择。
- Ada用case/when结构。
- Shell的case语句使用通配符测试模式,可以使用形如*.txt匹配所有以.txt结尾的文本,这比编译型语言更自由。Pattern还可用|组合做多重匹配。
- PHP作为脚本语言,case参数可以是字符串。
- Kotlin 用when代替以往的switch,例如:
when (a) {
1 -> println("a is 1")
2 -> println("a is 2")
else -> println("a is not 1, 2")
}
when (a) {
1, 2 -> println("a is 1 or 2")
else -> println("a is not 1, 2")
}
when {
a == 1 -> println("a is 1")
a == 2 -> println("a is 2")
else -> println("a is not 1, 2")
} - Pascal 采用 case xxx of 开头,后面紧跟不同case以及else. 例如:
case ch of
'a' : Writeln("a");
else: Writeln("not a");
end;- case参数可以用逗号分隔多个参数,也可以用 .. 指代连续范围。
- 仓颉 提供match/case条件匹配。
switch结构起源
- 最早Algol 68引入了多重选择语句switch/case, 为后续大部分编程语言所用,比如C/C++/Java/C#.
- Fortran最早没有,在Fortran 77版本引入了类似的select/case语句。
- Cobol 85引入了evaluate语句,类似于switch/case.
- Ada支持case一条语句多条件和范围设定,形如when 1 | 2 | 10 .. 15表示为1或2或10~15的范围。
- 标准C语言不支持case语句范围,GNU C扩展了此功能,示例:case 10 ... 1000: 表示在10到100范围内。
Fall Through
case贯穿在某些时候特别有效,比如libc %i用%d同一套输出逻辑,仅仅将%i的case和%d地方一起即可。
- C/C++有着近乎汇编的控制能力, 对fall through的支持最自由。
- Java也支持贯穿,因不支持goto对于某些处理逻辑不那么灵活。
- C#更有意思,支持贯穿,但必须用goto语句显式指明跳转哪里。
若文章对您有帮助,欢迎关注 程序员小迷 。助您在编程路上越走越好!
微风不燥,阳光正好,你就像风一样经过这里,愿你停留的片刻温暖舒心。
我是 程序员小迷 (致力于C、C++、C#、Android、iOS、Java、Kotlin、Objective-C、Swift、Shell、JavaScript、TypeScript、Python等编程技术的技巧经验分享),若作品对您有帮助,请关注、分享、点赞、收藏、在看、喜欢,您的支持是我们为您提供帮助的最大动力。