【Swift 3 && C++11】<第一部分> 概覽(3): Swift函數(shù)和閉包 與 C++函數(shù)和lamdba表達(dá)式

函數(shù)調(diào)用時(shí)的棧

函數(shù)就是一個(gè)可調(diào)用的代碼塊. C++中把它歸為可調(diào)用對(duì)象的一種, 而 Swift 中把函數(shù)歸為一種特殊的閉包.

由于 C++包含了大部分的 C, 在它其中的函數(shù)形式有多種, 但都沒(méi)有 Swift 中的函數(shù)簡(jiǎn)單強(qiáng)大: 簡(jiǎn)單是因?yàn)?Swift 中函數(shù)的形式都?xì)w為一種 —— 后置返回樣式; 強(qiáng)大是因?yàn)?Swift 中函數(shù)可以嵌套 —— 函數(shù)中也可以定義函.,函數(shù)可以嵌套的玄機(jī)大概是閉包可以嵌套吧.

而 C++中相當(dāng)于閉包的lamdba表達(dá)式實(shí)際上卻是一個(gè)未命名類的未命名對(duì)象, 所以它也和C++的函數(shù)一樣被 C++歸為可調(diào)用對(duì)象的一種.(注意在 C++14對(duì) lamdba 表達(dá)式作了優(yōu)化升級(jí))

所以, 看著這里, 你大概有一個(gè)基本的看法: ** lamdba 和 閉包根本就是兩個(gè)東西, 但是在功能上, 它們的作用又很相似.** 注意名詞: 匿名函數(shù), lamdba, 閉包

形式 Swift C++
函數(shù) func關(guān)鍵字開(kāi)頭 C 風(fēng)格函數(shù)和 auto 開(kāi)頭的尾置返回類型的函數(shù): ->
閉包與 lamdba 符號(hào){}創(chuàng)建閉包 **[捕獲列表] (參數(shù)列表:可忽略) -> 返回類型:可忽略 {函數(shù)體} **

函數(shù)

Swift 中使用func 來(lái)聲明一個(gè)函數(shù),使用名字和參數(shù)來(lái)調(diào)用函數(shù), 使用->來(lái)指定函數(shù)返回值的類型.

func greet(person: String, day: String) -> String {
    return "Hello \(person), today is \(day)."
}
greet(person: "Bob", day: "Tuesday")

練習(xí): 刪除 day 參數(shù),添加一個(gè)參數(shù)來(lái)表示今天吃了什么午飯。

默認(rèn)情況下,函數(shù)使用它們的參數(shù)名稱作為其參數(shù)的標(biāo)簽。 在參數(shù)名稱前寫入自定義參數(shù)標(biāo)簽,或者寫入 _ 以使用無(wú)參數(shù)標(biāo)簽。

func greet(_ person: String, on day: String) -> String {
    return "Hello \(person), today is \(day)."
}
greet("John", on: "Wednesday")

C++中的函數(shù)形式則跟經(jīng)典的 C 函數(shù)沒(méi)啥區(qū)別, C++11可以使用尾置返回類型,使用調(diào)用運(yùn)算符()并傳入必須的實(shí)參來(lái)執(zhí)行函數(shù), 唯一 沒(méi)有 Swift給力的就是沒(méi)有函數(shù)標(biāo)簽, 造成的結(jié)果就是一般來(lái)說(shuō)閱讀別人的代碼很難推測(cè)出相應(yīng)函數(shù)的功能:

#include <iostream>
#include <string>
using namespace std;

auto greet(string person, string day) -> string { // 使用尾置返回類型定義一個(gè)函數(shù), auto 開(kāi)頭
    return "Hello " + person + ", today is " + day + ".";
}

int main() { // 這里的main 函數(shù)就是一個(gè)經(jīng)典的有返無(wú)參的 C 風(fēng)格函數(shù)
    
    greet("Bob", "Tuesday");
    
    return 0;
}

Swift 中可以使用元組來(lái)創(chuàng)建一個(gè)復(fù)合值, 比如說(shuō)從一個(gè)函數(shù)中返回多個(gè)值. 元組中的元素可以通過(guò)名字或數(shù)字被引用:

func calculateStatistics(scores: [Int]) -> (min: Int, max: Int, sum: Int) {
    var min = scores[0]
    var max = scores[0]
    var sum = 0

    for score in scores {
        if score > max {
            max = score
        } else if score < min {
            min = score
        }
        sum += score
    }

    return (min, max, sum)
}
let statistics = calculateStatistics(scores: [5, 3, 100, 3, 9])
print(statistics.sum)
print(statistics.2)

C++中也可以使用元素來(lái)創(chuàng)建一個(gè)復(fù)合值, 利用它來(lái)返回多個(gè)值. 只是 C++元組的成員都是未命名的, 而且只能標(biāo)準(zhǔn)庫(kù)模板函數(shù)get來(lái)引用其中的元素:

#include <iostream>
#include <tuple> // 包含 tuple 頭文件來(lái)創(chuàng)建元組
#include <vector>

using namespace std;

auto calculateStatistics(vector<int> scores) -> tuple<int, int, int> { // 返回元組的函數(shù)
    auto min = scores[0];
    auto max = scores.front(); // 等同于scores[0]
    auto sum = 0;
    
    for (auto score : scores) {
        if (score > max) {
            max = score;
        }else if (score < min) {
            min = score;
        }
        sum += score;
    }
    
    return make_tuple(min, max, sum); // 可以使用 make_tuple 標(biāo)準(zhǔn)庫(kù)函數(shù)生成元組對(duì)象
}

int main() {
    
    vector<int> scores = {5, 3, 100, 3, 9};
    auto statistics = calculateStatistics(scores);
    cout << get<2>(statistics) << endl; // 使用標(biāo)準(zhǔn)庫(kù)函數(shù)模板來(lái)訪問(wèn)元組中的元素, 元組中的元素下標(biāo)從0開(kāi)始.
    
    return 0;
}

Swift 中的函數(shù)可以帶有可變個(gè)數(shù)的參數(shù), 這些參數(shù)在函數(shù)內(nèi)表現(xiàn)為數(shù)組:

func sumOf(numbers: Int...) -> Int {
    var sum = 0
    for number in numbers {
        sum += number
    }
    return sum
}
sumOf()
sumOf(numbers: 42, 597, 12)

C++中的函數(shù)同樣也可以帶有可變個(gè)數(shù)的參數(shù):


#include <iostream>
#include <initializer_list> // 包含initializer_list,來(lái)使用initializer_list類型定義含有可變形參的函數(shù)
using namespace std;

// 1, 所用實(shí)參類型相同, 可以使用initializer_list
auto sumOf(initializer_list<int> numbers = {}) -> int { // 此時(shí)參數(shù)都是 int 類型的值的序列, 可以在<>中間寫入?yún)?shù)的類型, 其中 = {}為默認(rèn)參數(shù)
    auto sum = 0;
    for (auto number : numbers) {
        sum += number;
    }
    return sum;
}

int main() {
    
    sumOf(); // 當(dāng)函數(shù)沒(méi)有傳入任何實(shí)參時(shí), 將使用默認(rèn)參數(shù)
    sumOf({42, 597, 12}); // 在大括號(hào)中放入要傳遞的一個(gè)值的序列

    return 0;
}

練習(xí): 分別用兩種語(yǔ)言寫一個(gè)計(jì)算參數(shù)平均值的函數(shù)。

Swift 中函數(shù)可以嵌套. 被嵌套的函數(shù)來(lái)重構(gòu)一個(gè)太長(zhǎng)或者太復(fù)雜的函數(shù). 函數(shù)是一等類型, 這意味著函數(shù)可以作為另一個(gè)函數(shù)的返回值. 函數(shù)也可以當(dāng)做參數(shù)傳入另一個(gè)函數(shù).

func returnFifteen() -> Int { 
    var y = 10
    func add() { // 一個(gè)函數(shù)可以嵌套在另一個(gè)函數(shù)中
        y += 5
    }
    add()
    return y
}
returnFifteen()

func makeIncrementer() -> ((Int) -> Int) { // 函數(shù)可以返回另一個(gè)函數(shù)
    func addOne(number: Int) -> Int {
        return 1 + number
    }
    return addOne
}
var increment = makeIncrementer()
increment(7)

func hasAnyMatches(list: [Int], condition: (Int) -> Bool) -> Bool { // 函數(shù)的參數(shù)可以是一個(gè)函數(shù)
    for item in list {
        if condition(item) {
            return true
        }
    }
    return false
}
func lessThanTen(number: Int) -> Bool {
    return number < 10
}
var numbers = [20, 19, 7, 12]
hasAnyMatches(list: numbers, condition: lessThanTen)

C++中除了不能在一個(gè)函數(shù)內(nèi)部嵌套定義函數(shù)之外, 它還不允許返回另一個(gè)函數(shù)、函數(shù)的參數(shù)也不能是函數(shù) —— 盡管如此, 但是我們?nèi)匀豢梢苑祷刂赶蚝瘮?shù)的指針,帶有指向函數(shù)的指針的形參來(lái)達(dá)到同樣的目的:

#include <iostream>
#include <vector>

using namespace std;

auto add(int y) -> int {
     return y += 5;
}

auto returnFifteen(int y) -> int { // 函數(shù)不能嵌套, 但是函數(shù)內(nèi)部仍然可以調(diào)用另一個(gè)函數(shù)
    return add(y);
}

auto addOne(int number) -> int {
    return 1 + number;
}

// 函數(shù)可以返回指向函數(shù)的指針
auto makeIncrementer() -> int (*) (int) { // int (*) (int) 為指向返回int, 帶有一個(gè)參數(shù)為 int參數(shù)的函數(shù)指針
    return addOne;
}

auto lessThanTen(int number) -> bool {
    return number < 10;
}

// 函數(shù)可以帶有指向函數(shù)的形參
auto hasAnyMatches(vector<int> list, decltype(lessThanTen) *condition) -> bool { // decltype(lessThanTen) 是得到一個(gè)函數(shù)的類型的簡(jiǎn)單方法, 注意此時(shí)只是得到函數(shù)的類型, 寫函數(shù)指針則只需要在后面寫個(gè) * 即可
    for (auto item : list) {
        if (condition(item)) {
            return true;
        }
    }
    
    return false;
}

    
int main() {
    
    returnFifteen(10);
    
    auto increment  = makeIncrementer();
    increment(7);
    
    vector<int> numbers = {20, 19, 7, 12};
    hasAnyMatches(numbers, lessThanTen);
    
    return 0;
}

Swift 閉包 和 C++ lamdba 表達(dá)式

Swift 中函數(shù)實(shí)際上是一種特殊的閉包: 它是一段能夠在之后被調(diào)用的代碼.閉包中的代碼能訪問(wèn)閉包所創(chuàng)建作用域中能得到的變量和函數(shù), 即使閉包是在不同的作用域被執(zhí)行的 —— 在嵌套函數(shù)例子中所看到的那樣. 你可以使用{}來(lái)創(chuàng)建一個(gè)匿名的閉包. 使用in 進(jìn)參數(shù)和返回值類型聲明與閉包函數(shù)體進(jìn)行分離:

numbers.map({
    (number: Int) -> Int in
    let result = 3 * number
    return result
})

練習(xí): 重寫閉包,對(duì)所有奇數(shù)返回0。

有幾種方法可以寫簡(jiǎn)潔的閉包. 當(dāng)閉包的類型已知—— 就像在代理的回調(diào) —— 你可以忽略它的參數(shù)類型、返回值類型之一或者都忽略。而只有單獨(dú)一條語(yǔ)句的閉包隱式地返回該語(yǔ)句的值:

let mappedNumbers = numbers.map({ number in 3 * number })
print(mappedNumbers)

你可以通過(guò)數(shù)字而不是通過(guò)名字來(lái)引用參數(shù) —— 在非常短的閉包中非常有用. 當(dāng)一個(gè)閉包作為最后一個(gè)參數(shù)傳遞給一個(gè)函數(shù)時(shí), 它可以直接跟在括號(hào)后面. 當(dāng)一個(gè)閉包是傳給函數(shù)的唯一參數(shù)時(shí), 你可以完全忽略括號(hào):

let sortedNumbers = numbers.sorted { $0 > $1 }
print(sortedNumbers)

C++中lamdba 表達(dá)式實(shí)際上是一個(gè)未命名類的未命名對(duì)象 —— 是一個(gè)可調(diào)用的函數(shù)對(duì)象.一個(gè) lamdba 表達(dá)式表示一個(gè)可調(diào)用代碼單元.形式如: [捕獲列表] (參數(shù)列表) -> 返回類型 { 函數(shù)體 }, lamdba 必須使用尾置返回來(lái)指定返回類型. 雖然一個(gè) lamdba 可以出現(xiàn)在一個(gè)函數(shù)中, 使用其局部變量, 但它只能使用那些明確指明的變量. lamdba 通過(guò)捕獲列表來(lái)包含我們需要使用的局部變量, 當(dāng)然我們可以讓lamdba 隱式捕獲變量, 在捕獲列表中寫一個(gè)&=. &告訴編譯器采用捕獲引用方式, =告訴編譯器采用值捕獲方式,C++14已經(jīng)給 lamdba 做出了升級(jí)和優(yōu)化:

#include <iostream>
#include <vector>
using namespace std;

int main() {
    
    auto otherCompareValue = 10;
    vector<int> numbers = {20, 19, 7, 12};
    sort(numbers.begin(), numbers.end(), [=](const int &a, const int &b) -> bool { // [=]表示采用值捕獲方式, const int &a 中 &a 表示引用這個(gè)參數(shù). 你可以把[=]中的=替換為otherCompareValue來(lái)明確指出我要捕獲otherCompareValue這個(gè)局部變量.
        if (a < b && a < otherCompareValue) {
            cout << a << "小于" << otherCompareValue << endl;
        }
        return a < b;
    });
    
    for (auto i : numbers) {
        cout << i << " ";
    }
    
    return 0;
}```
我們可以忽略參數(shù)列表和返回類型, 但必須永遠(yuǎn)包含捕獲列表和函數(shù)體.如果忽略返回類型, `lambda `根據(jù)函數(shù)體中的代碼來(lái)推斷出返回類型 —— 先別覺(jué)得它智能  —— 如果函數(shù)只是一個(gè)` return `語(yǔ)句, 則返回類型從返回的表達(dá)式推斷而來(lái), 否則返回類型為 `void`:

include <iostream>

using namespace std;

int main() {

auto f = [] {return 42;};
cout << f() << endl;

return 0;

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,698評(píng)論 6 539
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,202評(píng)論 3 426
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 177,742評(píng)論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 63,580評(píng)論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,297評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 55,688評(píng)論 1 327
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,693評(píng)論 3 444
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 42,875評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,438評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,183評(píng)論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,384評(píng)論 1 372
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,931評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,612評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 35,022評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 36,297評(píng)論 1 292
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,093評(píng)論 3 397
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,330評(píng)論 2 377

推薦閱讀更多精彩內(nèi)容