Swift closure & higher order functions - 斯坦福iOS课程精简笔记

正在学swift以及iOS,学习过程中发现swift语言中有许多精妙之处。上课时closure这个swift的精华和重要概念没讲清楚,去看了Stanford公开课受益匪浅,在此记下备忘。因为我之前的主要语言是Java,也站在一个Javaer的角度谈谈Swift中不同的概念。

Function Type

在说closure之前,首先说下swift中的function。swift中的function是一个type,也就是说可以作为一个变量存在。例如:

var operation : (Double) -> Double

这里声明了一个名为“operation”的变量,而他的类型是一个拥有一个Double参数,并返回一个Double值的function。既然是变量,就可以赋值,因此,你可以将任何一个“拥有一个Double参数,并返回一个Double值”的function赋值给这个“operation”变量。例如:

operation = sqrt

这样就把sqrt(平方根)这个函数赋值给了operation这个变量,现在我们就可以使用这个operation函数了,使用方法和正常的function相同:

let result = operation(2.25)     // result will be 1.5

关于Closure

首先closure的概念, 可以说是一个没有名字的、in-line 的function,他的存在是为了简化代码的。比如,当你想实现一个简单的函数,并且这个函数仅在某个地方需要,以后不太可能会用到。这时你单独先一个function就很麻烦了,于是,closure站立了出来。举例来说,你想实现一个把double取负的function,没有closure时你可能会这么做:

func changeSign(num : Double) -> Double {return -Double}

let operation = changeSign

这里特意把changeSign这个函数写在一行,因为想展示从一个普通func怎么转化为closure。接下来我们试着直接把函数体赋值给operation这个变量从而简化代码:

let operation = (num : Double) -> Double {return -num}         // syntax incorrect

这里直接把上面changeSign后面斜体的函数体搬了过来,然而此时语法还不对,需要再做点微调:

let operation  = {(num : Double) -> Double in return -num}     // this is a closure

注意上面代码中斜体并加粗的部分:1.把函数体的大括号移到最前面  2.用关键词in替换原来大括号的位置。此时,大括号包住了整合closure,大括号以及内部就是一个closure。注意关键词in,它并不表示英语里“in”的意思,而是可以看做分割该函数(closure也是一个函数)的变量与返回值(此处为(num : Double) -> Double,即该函数的类型)函数体代码(此处为return -Double)的分界线,替代普通function中开大括号(open curly parenthesis)的作用。

以上就是一个最最基本、最最普通、最最“正经”的closure。然而我们并不满足,希望它更简化!首先,由于swift拥有type inference的能力,因而我们并不需要写明它的返回类型:

let operation  = {(num : Double) in return -num}

还是因为type inference,我们也不需要写明输入参数的类型:

let operation  = {(num) in return -num}

我们也知道,closure一定会返回一个值,所以return这个关键词也没必要写上了:

let operation  = {(num) in -num}

想变量名是一件很麻烦的事,我们很懒所以并不想做这件事。closure允许你在函数体中用$0, $1, $2...分别代表第一个参数,第二个参数,第三个参数。由于我们连参数都没写,也就不需要in这个关键词了,只需要函数体即可:

let operation = { -$0 }

还想继续简化?-- 你咋不上天呢?

因为closure是function,而function是一种type,因而closure可以作为函数参数,这在swift中也很常见。例如:


High Order Function - Closure的妙用之一

Higher Order Function (以下简称HOF) 是 functional programming中的重要函数。Swift中的主要HOF有map, filter, reduce, flatmap等。

如果知道mapreduce,这些函数的作用应该不会陌生。Swift中的higher order function是Collection的函数,这些HOF的参数都是一个closure,返回值是一个Array。HOF会逐个作用于该collection中的每一个元素,并把该closure以该元素为参数的返回值append到返回的Array中。

map函数

map的作用是把collection中的元素变换成另一个元素,由于只是一个变换,因而返回的Array长度和原collection的元素数量相同。

let numbers = [1,6,2,15,2,8]

let negativeNumbers = numbers.map( {(element: Int) -> Int in return -element} )                 // 返回 [-1,-6,-2,-15,-2,-8]

利用前面提到的简化,可以逐步简化:

let negativeNumbers = numbers.map( {element in -element} )

let negativeNumbers = numbers.map( { -$0 } )

上面提到,HOF会逐个作用于collection的每个元素,所以此处的$0就是collection中的一个元素,而closure的返回值-$0会被逐个添加到map函数返回的Array中。上面的代码同样也可以写成下面两种形式:

let negativeNumbers = numbers.map(){ -$0 }

let negativeNumbers = numbers.map{ -$0 }

上面这两种语法都对,因为swift允许:1. 当一个closure是函数的最后一个参数时,你可以把它移到小括号外面 2. 当closure是函数的唯一一个参数时,你连那个小括号也不需要了。由于规则1,我们也很常见swift中的函数的最后一个参数是一个closure。

filter函数

filter顾名思义,过滤掉collection中的一些元素。filter函数中的closure依然逐个作用于collection中的每一个元素,返回一个bool值。当该closure返回true时,该元素会被添加到filter函数返回的Array中,而若closure返回为false,则该元素不会被添加,也就是被“过滤“掉了。

let numbers = [1,6,2,15,2,8]

let oddNumbers = numbers.filter{ $0 == 2 }    //返回 [6,2,2,8]

当一个元素$0符合 $0 == 2 的条件时,它会被添加到返回的array中。

reduce函数

reduce函数比前两个稍微复杂一点。它对collection中也是对所有元素逐个进行操作,但它的返回值只有一个,也就是说把所有元素reduce成一个。因此,在对元素进行操作后,它会把操作的结果记下来,在下一次调用该closure时(对下一个元素操作时)作为一个参数传入。因此,reduce函数的closure有两个参数,$0是前一次操作完的结果,$1是当前操作对象元素。同时,reduce函数本身也有两个参数,第一个参数是一个initial value,第二个参数是closure。由于closure对第一个元素操作时没有前一次操作的结果,所以需要这个initial value来作为closure对第一个对象操作时的$0

let numbers = [1,6,2,15,2,8]

let sum = numbers.reduce( initialValue : Int, { prevSum : Int, currElement  : Int in  return prevSum + currElement } )     //返回 34

对numbers[0]操作时,prevSum = initialValue, currElement = numbers[0], closure返回0 + 1 = 1, 并把返回值1赋值给prevSum: prevSum = 1。

对numbers[1]操作时, prevSum = 1, currElement = numbers[1], closure返回1 + 6 = 7。以此类推。

也可简写成:

let sum = numbers.reduce(0, {$0 + $1})

let sum = numbers.reduce(0) {$0 + $1}

还有一种比较变态的简化,因为reduce知道你要在$0和$1之间进行操作,所以你可以不用写他们俩,只写一个操作符:

let sum = numbers.reduce(0, +)

flatMap - 太晚了,明天填坑

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

推荐阅读更多精彩内容