Swift vs. Kotlin 漫谈之控制流

Kotlin 君和 Swift 君在一个团队一起开发已经很久了,由于平台的差异性,他们经常会进行一些技术上的交流(PK),「Kotlin vs. Swift」系列就是他们在互相切磋时的语录。

技术漫谈

Kotlin:

Hi,Swift 君,今天我们继续煮酒论道吧

Swift:

好,那我们今天就切磋一下控制流吧, 你们是怎么做条件判断的?

Kotlin:

我们用 if 做条件判断,一般传统的形式是这样的:

var max: Int
if (a > b) {
    max = a
} else {
    max = b
}

Swift:

嗯,这与我们一样,我们可以省略 if 后面的小括号 ()

Kotlin:

但是我们 if 语句可以用作表达式,可以有值返回,如 val b = if (a > 0) a else 0

Swift:

这也没啥,与我们的三目运算符如出一辙,var b = a > 0 ? a : 0
半斤八两,没啥好炫耀的.

Kotlin:

额。。。好吧,那我们 if 做表达式的时候后面也可以接代码块,在块代码中随后一行的输出会作为该块代码的返回值,如下:

val max = if (a > b) {
    print("Choose a")
    a
} else {
    print("Choose b")
    b
}

你行吗?

Swift:

这。。。,算你牛×

Kotlin:

哈哈,服了吧,需要注意的是,if 在用作表达式的时候,后面必须提供 else 从句。

Swift:

哦,我们的 if 语句和大多数语言类似,没有啥花哨的东西,但是我们除了 if 做条件判断外,还可以用 guard 语句, 形式如下:

guard <condition> else {
    <statements>
}

Kotlin:

这看着和 if 语句差不多嘛,有啥区别呢?

Swift:

举个栗子你就明白了

if 版本:
func greet(person: [String: String]) {
    if let name = person["name"] {
        print("Hello \(name)")
        
        if let location = person["location"] {
            print("weather is nice in \(location).")
        } else {
            print("weather is nice near you.")
        }
    } else {
        print("Hello stranger")
    }
}

guard 版本:
func greet(person: [String: String]) {
    guard let name = person["name"] else {
        print("Hello stranger")
        return
    }
    
    print("Hello \(name)")
    
    guard let location = person["location"] else {
        print("weather is nice near you.")
        return
    }
    
    print("weather is nice in \(location).")
}

对比可以看出,guard 版本更清晰易读,if 版本的一连串的 if/else 让人头晕。

Kotlin:

赞,说白了,guard 就是把不符合条件的处理事件前置,提前退出,避免了代码金字塔的出现,使代码看起来更优雅,更易读。不过跟你说个小秘密,我们自己封装个类似 guard 的函数。

Swift:

2333,不过我们使用 guard 语句的时候也必须要有 else 从句

Kotlin:

嗯嗯,来说说你们是怎么做条件匹配的

Swift:

我们用 switch 语句来进行条件匹配,例如:

let x = 1

switch x {
case 1:
    print("x == 1")
case 2:
    print("x == 2")
default:
    print("x is neither 1 nor 2")
}

Kotlin:

差不多,我们是用 when 来进行条件匹配,意思是一样的:

when (x) {
    1 -> print("x == 1")
    2 -> print("x == 2")
    else -> { // 注意这个块
        print("x is neither 1 nor 2")
    }
}

Swift:

嗯~ o(* ̄▽ ̄*)o,语法不一样而已,看上去你们的更简洁一些嘛。

Kotlin:

当有不同的分支需要同样的处理的话,可以用逗号分隔写在一起。

when (x) {
    0, 1 -> print("x == 0 or x == 1")
    else -> print("otherwise")
}

Swift:

我的写法也一样:

switch x {
case 0, 1:
    print("x == 0 or x == 1")
default:
    print("otherwise")
}

当匹配到的 case 中的代码执行完毕后,switch 语句会直接退出,而不会继续执行下一个 case ,如果确实想执行下一个 case,我们也提供了相应的方法。

Kotlin:

这也可以?什么方法?

Swift:

就是显式的在当前 case 中使用 fallthrough 语句。例如:

let x = 5
var description = "\(x) is"
switch x {
case 2, 3, 5, 7, 11:
    description += " a prime number, and also"
    fallthrough
default:
    description += " an integer."
}
print(description)
// 输出 "5 is a prime number, and also an integer."

Kotlin:

我们没有这个功能,不过这种贯穿功能似乎也不常用嘛。

Swift:

是的,不常用不代表没有用嘛。switch 还可以匹配一个值的区间。例如:

switch x {
case 1..<10:
    print("single digit")
case 10...99:
    print("double digit")
default:
    print("out of range")
}

Kotlin:

when 也可以:

when (x) {
    in 1..10 -> print("x is in the range")
    in validNumbers -> print("x is valid")
    !in 10..20 -> print("x is outside the range")
    else -> print("none of the above")
}

Swift:

哇~, 你们还可以直接在一个匹配模式之前加 ! 操作符,好强大,我们不可以。

Kotlin:

牛吧,不仅如此,when 可以不提供参数,直接顺序匹配满足条件的表达式后,执行其对应后面的表达式。

when {
    x.isOdd() -> print("x is odd")
    x.isEven() -> print("x is even")
    else -> print("x is funny")
}

Swift:

厉害了,我们不行,我们 switch 后必须要有参数。

Kotlin:

哈哈,服了吧

Swift:

不服,看来不给你点厉害的瞧瞧,不知道马王爷有几只眼!

Kotlin:

(⊙ˍ⊙)

Swift:

我们可以使用元组在同一个 switch 语句中测试多个值。元组中的元素可以是值,也可以是区间。另外,可以使用下划线(_)来匹配所有可能的值。例如:

let somePoint = (1, 1)
switch somePoint {
case (0, 0):
    print("(0, 0) is at the origin")
case (_, 0):
    print("(\(somePoint.0), 0) is on the x-axis")
case (0, _):
    print("(0, \(somePoint.1)) is on the y-axis")
case (-2...2, -2...2):
    print("(\(somePoint.0), \(somePoint.1)) is inside the box")
default:
    print("(\(somePoint.0), \(somePoint.1)) is outside of the box")
}
// 输出 "(1, 1) is inside the box"

Kotlin:

赞!我们没有元组类型,虽然 Kotlin 也提供了类似的析构功能,但还是没有你们这么灵活,所以也就没有这种模式匹配。

Swift:

不仅如此,case 分支还允许将匹配的值绑定到一个临时的常量或变量,并且在 case 分支体内使用:

let anotherPoint = (2, 0)
switch anotherPoint {
case (let x, 0):
    print("on the x-axis with an x value of \(x)")
case (0, let y):
    print("on the y-axis with a y value of \(y)")
case let (x, y):
    print("somewhere else at (\(x), \(y))")
}
// 输出 "on the x-axis with an x value of 2"

Kotlin:

牛!

Swift:

switch 分支的模式还可以使用 where 语句来判断额外的条件。如下:

let yetAnotherPoint = (1, -1)
switch yetAnotherPoint {
case let (x, y) where x == y:
    print("(\(x), \(y)) is on the line x == y")
case let (x, y) where x == -y:
    print("(\(x), \(y)) is on the line x == -y")
case let (x, y):
    print("(\(x), \(y)) is just some arbitrary point")
}
// 输出 "(1, -1) is on the line x == -y"

这时候当且仅当控制表达式匹配一个 case 的模式且 where 子句的表达式为真时,case 中的语句才会被执行.

Kotlin:

请收下我的膝盖。。。

Swift:

哈哈,接下来我们切磋一下循环语句吧

Kotlin:

好,Kotlinfor 循环和 while 循环与 Java 里面的语法差别不大,不过得益于 Kotlin 的析构功能,我们可以在迭代中方便的使用多个参数。

for ((index, value) in array.withIndex()) {
    println("the element at $index is $value")
}

Swift:

我们也有类似的写法。

for (index, value) in array.enumerated() {
    print("the element at \(index) is \(value)")
}

除了 for-in语句外, 我们也有 whilerepeat-while 来实现循环遍历。

Kotlin:

你的 repeat-while 是不是和我们的 do-while 一样?

Swift:

是的,一模一样。

Kotlin:

Kotlin 提供了灵活的标签功能,在跳转表达式出现歧义的时候,可以支持以标签的方式制定跳转表达式要跳转的位置,如在嵌套循环中。例如:

loop@ for (i in 1..100) {
    for (j in 1..100) {
        if (……) break@loop
    }
}

Swift:

我们也有类似的标签功能。

var sum = 0

rowLoop: for row in 0..<8 {
  columnLoop: for column in 0..<8 {
    if row == column {
      continue rowLoop
    }
    sum += row * column
  }
}

Kotlin:

嗯嗯,看上去就是标签的定义方式不一样,我们要多写一个 @.

不过我们的标签还可以用在函数中,例如:

fun foo() {
    ints.forEach lit@ {
        if (it == 0) return@lit
        print(it)
    }
}

Swift:

我们不能

Kotlin:

甚至我们还可以使用隐式的标签。

fun foo() {
    ints.forEach {
        if (it == 0) return@forEach
        print(it)
    }
}

Swift:

嗯,你们的标签功能比我们强大。

Kotlin:

好了,今天我们就到此为止吧,和你交流还是收获颇丰的。

Swift:

(^-^)V,青山不改,绿水长流,咱们江湖再见。

Kotlin:

后会有期。

知识点总结

Kotlin

条件判断

在 Kotlin 中用 if 做判断有两种用法,一种是用作传统的条件判断,另一种是做为表达式。

当用作传统的条件判断的时候,跟 Java 没有什么差异。

var max: Int
if (a > b) {
    max = a
} else {
    max = b
}

if 用作表达式的时候,可以有值返回,如 val b = if (a > 0) a else 0。这样的用法很像 Java 里的三目运算,所以在 Kotlin 里面就没有再单独提供三目运算了。

if 作为表达式的时候后面也可以接块代码,在块代码中随后一行的输出会作为该块代码的返回值。

val max = if (a > b) {
    print("Choose a")
    a
} else {
    print("Choose b")
    b
}

需要注意的是,在用作表达式的时候,后面必须提供 else

when 表达式

Kotlin 用 when 表达式替代 Java 里面的 switch-case,不过不仅仅是替代,when 表达式提供了更加强大的功能。

普通的条件匹配
when (x) {
    1 -> print("x == 1")
    2 -> print("x == 2")
    else -> { // 注意这个块
        print("x is neither 1 nor 2")
    }
}

when 会顺序比较所有的条件,匹配到合适的以后机会执行条件后面对应的表达式(类似 if也可以是块),当所有的条件都不满足的时候就会执行 else 后面的表达式。所有的when 表达式中都要提供 else,除非 Kotlin 能推断出不会再有其他条件存在。

当有不同的分支需要同样的处理的话,可以用逗号分隔写在一起。

when (x) {
    0, 1 -> print("x == 0 or x == 1")
    else -> print("otherwise")
}
匹配任意表达式

when 不仅支持常量的条件匹配,还可以支持任意表达式的匹配,所以你可以这样肆无忌惮的使用它来匹配某个数是否在某某范围。

when (x) {
    in 1..10 -> print("x is in the range")
    in validNumbers -> print("x is valid")
    !in 10..20 -> print("x is outside the range")
    else -> print("none of the above")
}

或者匹配未知类型的类型。

fun hasPrefix(x: Any) = when(x) {
    is String -> x.startsWith("prefix")
    else -> false
}
无参数匹配

when 可以不提供参数,直接顺序匹配满足条件的表达式后,执行其对应后面的表达式。

when {
    x.isOdd() -> print("x is odd")
    x.isEven() -> print("x is even")
    else -> print("x is funny")
}

循环

Kotlin 的 for 循环和 while 循环和 Java 里面的语法差别不大,不过得益于 Kotlin 的析构功能,我们可以在迭代中方便的使用多个参数。

for ((index, value) in array.withIndex()) {
    println("the element at $index is $value")
}

带标签的返回(return)、中断(break)和继续(continue)

Kotlin 提供了灵活的标签功能,在跳转表达式出现歧义的时候,可以支持以标签的方式制定跳转表达式要跳转的位置,如在嵌套循环中。

loop@ for (i in 1..100) {
    for (j in 1..100) {
        if (……) break@loop
    }
}

或者在函数中,

fun foo() {
    ints.forEach lit@ {
        if (it == 0) return@lit
        print(it)
    }
}

甚至我们还可以使用隐式的标签。

fun foo() {
    ints.forEach {
        if (it == 0) return@forEach
        print(it)
    }
}

Swift

条件判断

swift 使用 if 语句或 guard 语句来做条件判断。if 语句与 c语言等其他类型语句没有差别。guard 则是把不符合条件的处理事件前置,提前退出,避免了代码金字塔的出现,使代码看起来更优雅,更易读。

例如:

func greet(person: [String: String]) {
    guard let name = person["name"] else {
        return
    }
    
    print("Hello \(name)!")
    
    guard let location = person["location"] else {
        print("I hope the weather is nice near you.")
        return
    }
    
    print("I hope the weather is nice in \(location).")
}
 
greet(person: ["name": "John"])
// Prints "Hello John!"
// Prints "I hope the weather is nice near you."
greet(person: ["name": "Jane", "location": "Cupertino"])
// Prints "Hello Jane!"
// Prints "I hope the weather is nice in Cupertino."

不同于 if 语句,一个 guard 语句必须要有一个 else 从句与之配对。

Switch 语句

swiftswitch 语句来进行条件匹配,例如:

let x = 1

switch x {
case 1:
    print("x == 1")
case 2:
    print("x == 2")
default:
    print("x is neither 1 nor 2")
}

任何类型的值都可以作为控制表达式的值。当有不同的分支需要同样的处理的话,可以将这个几个值组合成一个复合匹配,并且用逗号分开,如下:

switch x {
case 0, 1:
    print("x == 0 or x == 1")
default:
    print("otherwise")
}

Fallthrough

当匹配到的 case 中的代码执行完毕后,switch 语句会直接退出,而不会继续执行下一个 case ,如果确实想执行下一个 case,需要显式的在当前 case 中使用 fallthrough 语句。例如:

let integerToDescribe = 5
var description = "The number \(integerToDescribe) is"
switch integerToDescribe {
case 2, 3, 5, 7, 11, 13, 17, 19:
    description += " a prime number, and also"
    fallthrough
default:
    description += " an integer."
}
print(description)
// 输出 "The number 5 is a prime number, and also an integer."
元组

可以使用元组在同一个 switch 语句中测试多个值。元组中的元素可以是值,也可以是区间。另外,可以使用下划线(_)来匹配所有可能的值。例如:

let somePoint = (1, 1)
switch somePoint {
case (0, 0):
    print("(0, 0) is at the origin")
case (_, 0):
    print("(\(somePoint.0), 0) is on the x-axis")
case (0, _):
    print("(0, \(somePoint.1)) is on the y-axis")
case (-2...2, -2...2):
    print("(\(somePoint.0), \(somePoint.1)) is inside the box")
default:
    print("(\(somePoint.0), \(somePoint.1)) is outside of the box")
}
// 输出 "(1, 1) is inside the box"

值绑定

不仅如此,case 分支还允许将匹配的值绑定到一个临时的常量或变量,并且在 case 分支体内使用:

let anotherPoint = (2, 0)
switch anotherPoint {
case (let x, 0):
    print("on the x-axis with an x value of \(x)")
case (0, let y):
    print("on the y-axis with a y value of \(y)")
case let (x, y):
    print("somewhere else at (\(x), \(y))")
}
// 输出 "on the x-axis with an x value of 2"

where 子句

switch 分支的模式可以使用 where 语句来判断额外的条件, 例如:

let yetAnotherPoint = (1, -1)
switch yetAnotherPoint {
case let (x, y) where x == y:
    print("(\(x), \(y)) is on the line x == y")
case let (x, y) where x == -y:
    print("(\(x), \(y)) is on the line x == -y")
case let (x, y):
    print("(\(x), \(y)) is just some arbitrary point")
}
// 输出 "(1, -1) is on the line x == -y"

这时候当且仅当控制表达式匹配一个 case 的模式且 where 子句的表达式为真时,case 中的语句才会被执行.

循环语句

Swift 中的循环语句包括:for-in, whilerepeat-while, while 与其他语言的 while 无二, repeat-while 则与 do-while 一致。

for-in 语句的形式如下:

for <item> in <collection> {
    <statements>
}

可以使用 for-in 语句来遍历一个序列,例如数组中的元素、数字范围或者字符串中的字符等.

使用 for-in语句遍历一个 Dictionary 来访问其键值对,如下:

let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
for (animalName, legCount) in numberOfLegs {
    print("\(animalName)s have \(legCount) legs")
}

for-in 还可以用来遍历一个数字范围,例如:

for index in 1...10 {
    print("\(index)")
}

带标签的语句

swift 中,可以使用标签来标记一个循环语句或 switch 语句。对于一个 switch 语句,可以使用 break 加标签的方式,来结束这个被标记的语句。对于一个循环语句,可以使用 breakcontinue 加标签,来结束或者继续执行这条被标记的语句。

var sum = 0

rowLoop: for row in 0..<8 {
  columnLoop: for column in 0..<8 {
    if row == column {
      continue rowLoop
    }
    sum += row * column
  }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容