switch
这个语法,相信所有碰过编程的小伙伴都不会陌生。是在众多老旧语言中,switch
都有着惊人的相似实现。但笔者一直觉得它的存在多少有点鸡肋,让我们先来看看Java
中的switch
:
public class SwitchDemo {
public static void main(String[] args) {
int score = 5;
String scoreLabel;
switch (score) {
case 1:
scoreLabel = "差评";
break;
case 2:
scoreLabel = "不满意";
break;
case 3:
scoreLabel = "一般";
break;
case 4:
scoreLabel = "还不错";
break;
case 5:
scoreLabel = "非常满意";
break;
default:
scoreLabel = "无效得分";
break;
}
System.out.println(scoreLabel);
}
}
Java
的这段switch
应该还是很有代表性的,就是从C
延续下来的写法,很多语言都是这么设计的。说实话,这一水的break
,看着还是有点难受,也不能说break
的设计完全没用,但是如果大部分情况下break
都是不能省的(省了逻辑就变了),那这个设计就值得商榷了。Java
程序员忍了多少年,情况终于在2019年发生了一点改变,以下是Java12
带来的新的语法:
String scoreLabel = switch (score) {
case 1 -> "差评";
case 2 -> "不满意";
case 3 -> "一般";
case 4 -> "还不错";
case 5 -> "非常满意";
default -> "无效得分";
};
这样写的确清爽很多,我们再来看看同样的问题,在Swift
中怎么实现:
let score = 5
var scoreLabel = ""
switch score{
case 1:
scoreLabel = "差评"
case 2:
scoreLabel = "不满意"
case 3:
scoreLabel = "一般"
case 4:
scoreLabel = "还不错"
case 5:
scoreLabel = "非常满意";
default:
scoreLabel = "无效得分";
}
print(scoreLabel)
猛一看,Swift
这版代码多了好多行,感觉没有Java12
这版优雅啊。这是因为Swift
里的switch
不能当作一个具备返回值的表达式放在等号的右边。但其实我并不觉得这是Swift
的缺点。因为在强类型语言中,任何数据的类型都是非常重要的,不可以朦胧不清。在Java12
的示例中,scoreLabel
是一个String
,但是等号右边的switch
如何保证自己的返回是一个String
呢?作为一个人类,纯看代码,其实并不能明确地看出来。当然,作为一个机器,编译器还是能看出来,比如我们稍微改下Java12
这版代码:
int score = 5;
String scoreLabel = switch (score) {
case 1 -> "差评";
case 2 -> "不满意";
case 3 -> "一般";
case 4 -> 3;
case 5 -> "非常满意";
default -> "无效得分";
};
System.out.println(scoreLabel);
在编译代码阶段,我们就会遇到一个报错提示:
java: 不兼容的类型: switch 表达式中的类型错误
int无法转换为java.lang.String
所以,不能说Java12
的设计有什么问题,反正编译器这边是清醒的。但是我个人总觉得,把等号左边的变量类型通过switch
穿透到其内部,来约束switch
里面的事情,有越俎代庖之嫌。
下面我们来改进Swift
的版本,看看能否得到近似于Java12
这般优雅的方式。
let score = 5
let scoreLabel = {()->String in
switch (score) {
case 1 : return "差评"
case 2 : return "不满意"
case 3 : return "一般"
case 4 : return "还不错"
case 5 : return "非常满意"
default : return "无效得分"
}
}()
print(scoreLabel)
我这个写法对于Swift
新手来说,稍微有点超纲了。等号右边是一个立即调用函数表达式,类似于JavaScript
里面的这种写法:
var result = (function () {
var name = "Barry";
return name;
})();
// IIFE 执行后返回的结果:
result; // "Barry"
我们说回Swift
,你大概能明白scoreLabel
的值就是等号右边函数执行过后的返回值。你可能会诧异为什么我写的函数
没有名字,因为这里是用Closure(闭包)
写成的匿名函数,被花括号包围的整个区域(包括花括号),也就是:{()->String in …… }
都是这个函数的定义,其中单词in
左侧的String
代表了这个函数的返回值,而in
之后的内容就是函数的逻辑代码了。在函数定义的}
之后,我放一对小括号()
,也就是这对小括号产生了神奇的效果,让函数获得了立即执行的魔力。
如果你不小心漏写了最后的小括号,比如这样:
let score = 5
let scoreLabel = {()->String in
switch (score) {
case 1 : return "差评"
case 2 : return "不满意"
case 3 : return "一般"
case 4 : return "还不错"
case 5 : return "非常满意"
default : return "无效得分"
}
}
print(scoreLabel)
你的得到的结果将会是:
(Function)
也就是说scoreLabel
这个常量存储的内容是个函数。而有了小括号,scoreLabel2
里面存的就是函数的返回值了,你将会看到如下输出:
非常满意
现在回过头来对比下Swift
与Java12
的两个实现版本,不难看出Swift
中通过一次函数返回值的明确声明,锁定了其内部的return
语句必须返回String
,不失为一个优雅的解决方案,与Java12
相比,可谓各有特色。但是考虑下面这种场景,Swift
交出来的答卷就有意思了:
score = 3
let scoreLabel3 = {()->String in
switch (score) {
case 1 ... 2 : return "差评"
case 3 ... 5 : return "好评"
default : return "无效得分"
}
}()
print(scoreLabel3)
如果我们只想给得分来个二分类,1到2分是差评、3到5分是好评。用上面的代码就会非常直观。并且case 1 ... 2 :
在此也可以写为case 1 ..< 3 :
,同样具有不错的表现力。
写到这一步,Java
其实就已经力不从心了。不过下面的代码才是Swift
独享的炫技时刻:
let vegetable = "red pepper"
switch vegetable {
case "celery":
print("Add some raisins and make ants on a log.")
case "cucumber", "watercress":
print("That would make a good tea sandwich.")
case let x where x.hasSuffix("pepper"):
print("Is it a spicy \(x)?")
default:
print("Everything tastes good in soup.")
}
这里是一位小伙伴在表达自己对各种蔬菜看法。我们先抛开他的个人喜好,注意代码中的第三段case
。它专门用来判断vegetable
是否以pepper
字样结尾,在本示例中,最终输出的代码为:
Is it a spicy red pepper?
通过这段输出,你应该已经明白x
的内容就是red pepper
,也就是说x
即是vegetable
。并且这里的x
仅仅是个代号,你可以换成任何其他你愿意使用的变量名。也就是说,case
后面的这个let
,就像魔术师的手,响指一打,你就会获得需要做switch
的判断的那个值。有了这个值,我们就可以基于它构建一个能够计算出布尔值的表达式,并把这个表达式放在where
之后。这样,当表达式的结果为true
时,这条case
语句也就命中了。并且不要忘记,刚才魔术师帮你拿到的x
,是可以在case
语句执行逻辑的区域内使用的。
显然swift
的这种switch
用法,给我们增加了很多的想象力。不过也许有人会说,这样的switch
是不是与if
的功能重叠有点多了?我觉得确实是这样,最早switch
被设计成一种if
的简化版本,主要用来针对等值
判断的,但是显然程序员们的愿望不止如此。所以我们才会在本世纪的一些新鲜语言里,看到更为激进的表现形式。
不过,语言层面提供的这些东西,就像佐料,至于真正做菜的时候加什么,放多少,还得是你这位程序员说的算。
阅读相关文章:Swift初体验(1)