[譯] Bob,函數語言程式設計是什麼鬼?

DeepMissea發表於2019-02-19

Bob,函數語言程式設計是什麼鬼?

寫給年輕的自己的教程

[譯] Bob,函數語言程式設計是什麼鬼?

老司機怎麼開車,我們就怎麼開

函數語言程式設計?

你懂的。很多人都討論它。你 Google 一下然後看了看前五篇文章,令人沮喪的是,你發現大部分文章只給出一個含糊不清的 Wikipedia 定義,像是:

“函數語言程式設計是一種程式設計正規化,能讓你的程式碼清晰又明確,沒有變數也沒有狀態。”

和你一樣,老兄,事實上,我也這樣搜尋過。我溫柔地做了個捂臉的表情,並輕聲迴應道:

這 TM 是啥?

先決條件

和閉包很像。如果你不理解什麼是後進和關鍵標識,比如 $0,那你還沒做好閱讀這篇教程的準備。你可以暫時離開,找這裡的資源來升升級。

非函數語言程式設計

我是十萬個為什麼。為什麼要學習函數語言程式設計?好吧,最好的答案往往來自於歷史。假設你要建立一個新增一個陣列的計算器應用。

// Somewhere in ViewController

let numbers = [1, 2, 3]
var sum = 0 

for number in numbers {
 sum += number
}複製程式碼

沒問題,但是如果我再新增一個呢?

// Somewhere in NextViewController 

let newNumbers = [4, 5, 6]
var newSum = 0

for newNumber in numbers {
 newSum += newNumber
}複製程式碼

這看起來就像我們重複我們自己,又長又無聊,還沒必要。你不得不建立一個 sum 來記錄新增的結果。這很讓人崩潰,五行程式碼。我們最好建立一個函式代替這些玩意。

func saveMeFromMadness(elements: [Int]) -> Int {
 var sum = 0

 for element in elements {
  sum += element
 }

 return sum
}複製程式碼

這樣在需要使用 sum 的地方,直接呼叫

// Somewhere in ViewController
saveMeFromMadness(elements: [1, 2, 3])

// Somewhere in NextViewController
saveMeFromMadness(elements: [4, 5, 6])複製程式碼

停下別動,對。你現在已經嘗試了一個函式式正規化的使用。函式式就是用函式來得到你想要的結果。

比喻

在 Excel 或者 Google 的 Spreadsheet 上,如果要對一些值求和,你需要選擇表格,然後呼叫一個像是 C# 編寫的函式。

[譯] Bob,函數語言程式設計是什麼鬼?

Excel 裡的求和函式

好,就是這樣,再見。感謝閱讀。 ?

宣告式 vs 命令式

最後,現在,是時候拿出詳細的函數語言程式設計定義了。

宣告式

我們經常把函數語言程式設計描述為宣告式的。你無須在意這個答案從何而來。舉個例子,一個人來爬珠穆朗瑪峰,可能從飛機上跳下去,也可能花好幾年從地下開始爬。你會得到同樣的結果。人們往往不知道 Excel 表格裡的 Sum 是怎麼組成的,但是,它就是得到相應的結果。

一個殘酷的例子,眾所周知的非函數語言程式設計,我們經常看到上面的呼叫被稱為命令式。它告訴你如何(how)得到從 A 到 B 的答案。

let numbers = [1, 2, 3]
var sum = 0

for number in numbers {
 sum += number
}複製程式碼

人們知道你迴圈了 numbers但是,這有必要麼?我不在意它是怎麼做的,我只在意結果的出來的迅速快捷。

因此,Excel 和 Spreadsheet 融合了函數語言程式設計的正規化,來更快更簡單的獲取結果,而不需要關心具體的實現。(我父親也沒必要在處理公司財務資料的時候關心它)

其他的好處

在上面那個讓人崩潰的例子裡,我們不得不建立一個 var sum = 0 來跟蹤每個檢視控制器的增加結果。但是這有必要嗎?sum 的值不斷改變,如果我弄亂了總和怎麼辦?而且,我在10 條 tips 讓你成為一個更好的 Swift 開發者中提到過,

更多的變數 → 更多記憶 → 更多頭痛 → 更多 bug → 更多的生活問題。

更多的變數 → 容易 TM 的 → 完蛋

因此,函式式正規化確保在使用的時候不可變或者沒有狀態的變化。

而且,和你意識到的或即將發現的一樣,函式式正規化提供了一個更利於維護程式碼的模型。

目的

那好,現在你明白了為什麼我們喜歡函數語言程式設計。所以呢?嗯,這篇教程,只專注於基本面。我不會討論函數語言程式設計在事件和網路等等中的應用。我可能會發一些通過 RxSwift 來做這些事的教程。所以說如果你是新手,跟著我,螺旋穩。

[譯] Bob,函數語言程式設計是什麼鬼?

(譯者配的圖 ? )

真正的工作

你可能已經見過像 filtermapreduce 等等的一些東西。不錯,這些讓你用函式式的途徑中的過濾器來處理一個陣列。確保你對泛型的理解同樣的酷。

這全是關於基本面的東西。如果我能教你如何在泳池裡游泳,那你也可以在海里,湖裡,池塘裡,泥坑裡(最好不是)游泳,這這篇教程,如果你學會了基本面,你就可以建立你自己的 mapreduce 或者其他你想要的炫酷函式。你可以 google 東西,否則,你不會從我這裡得到這個叫“Bob”的開發者的解釋了。

過濾器

假設你有個陣列。

let recentGrade = ["A", "A", "A", "A", "B", "D"] // My College Grade複製程式碼

你想要過濾/帶來並且返回一個只包含 “A” 的陣列,這能讓我媽媽感到快樂。你怎麼用命令式的方式來做這個?

var happyGrade: [String] = []

for grade in recentGrade {
 if grade == "A" {
  happyGrade.append(grade)
 } else {
  print("Ma mama not happy")
 }
}

print(happyGrade) // ["A", "A", "A", "A"]複製程式碼

這簡直讓人發瘋。我竟然寫了這種程式碼。我不會在校對的時候重新檢查,這很殘忍。檢視控制器中的8行程式碼??

不堪回首

我們必須停止這種瘋狂,並拯救所有像你這麼做的人。讓我們建立一個函式來完成它。振作起來。我們現在要對付一下閉包了。讓我們試著建立一個過濾器來完成和上面一樣的工作。真正麻煩現在來了。

函式式的方式簡介

現在我們建立一個函式,有一個包含 String 型別的陣列並且有個閉包,型別是 (String) -> Bool。最後,它返回一個過濾後的 String 陣列。為啥?忍我兩分鐘就告訴你。

func stringFilter(array: [String], returnBool: (String) -> Bool) -> [String] {}複製程式碼

你可能會對 returnBool 部分特別苦惱。我知道你在想什麼,

那麼,我們要在返回 Bool 下傳遞什麼?

你需要建立一個閉包,包含一個 if-else 語句來判斷陣列裡是否含有 “A”。如果有,返回 true

// A closure for returnBool 
let mumHappy: (String) -> Bool = { grade in return grade == "A" }複製程式碼

如果你想讓他更短,

let mamHappy: (String) -> Bool = { $0 == "A" }

mamHappy("A") // return true 
mamHappy("B") // return false複製程式碼

如果你對上面的兩個例子感到困惑,那你還適應不了這個副本。你需要鍛鍊一下然後再回來。你可以重讀我關於閉包的文章連結

由於還沒完成我們 stringFilter 函式的實現,讓我們從離開的位置繼續。

func stringFilter (grade: [String], returnBool: (String) -> Bool)-> [String] {

 var happyGrade: [String] = []
 for letter in grade {
  if returnBool(letter) {
   happyGrade.append(letter)
  }

 }
 return happyGrade
}複製程式碼

你的表情一定是 ?。我想說把刀放下,聽我解釋。通過 stringFilter 函式,你可以傳遞 mamHappy 作為 returnBool。然後呼叫 returnBool(letter),把每個項傳遞個 mamHappy,最終就是 mamHappy(letter)

它返回 true 或者 false。如果返回真,把 letter 加到只有 “A” 的 happyGrade 裡。? 這就是為什麼我媽媽在過去 12 年裡感到開心的原因。

不管怎樣,最終執行一下函式。

let myGrade = ["A", "A", "A", "A", "B", "D"]

let lovelyGrade = stringFilter(grade: myGrade, returnBool: **mamHappy**)複製程式碼

直接輸入閉包

其實你不需要建立一個分離的 mamHappy。可以直接在 returnBool 傳遞。

stringFilter(grade: myGrade, returnBool: { grade in
 return grade == "A" })複製程式碼

我想讓它更簡潔。

stringFilter(grade: myGrade, returnBool: { $0 == “A” })複製程式碼

肉和土豆

祝賀,如果你已經到了這裡,那你已經做到了。我很感謝你的關注。現在讓我們建立一個野蠻點的,廣為人知的通用過濾器,你可以建立一堆你想要過濾的。比如,過濾你不喜歡的單詞,過濾陣列裡大於 60 小於 100 的數。過濾只包含真值的布林型別。

最棒的是它用一句話就可以形容。我們拯救了生命和時間。愛它,我們可以努力工作,但是我們要聰明的努力工作。

泛型程式碼

如果你對泛型程式碼感到不適,那你現在所在的位置並不正確,這裡車速很快,趕快到安全的地方,名字是“Bob,泛型是什麼鬼?”,然後帶點武器回來繼續。

我要建立一個含有 Bob 泛型的函式,你可以使用 T 或者 U。但是你要知道,這是我的文章。

func myFilter<Bob>(array: [Bob], logic: (Bob) -> Bool) -> [Bob] {
 var result: [Bob] = []
 for element in array {
  if logic(element) {
   result.append(element)
  }
 }
 return result
}複製程式碼

讓我們試著找點聰明的學生

應用到學校系統

let AStudent = myFilter(array: Array(1...100), logic: { $0 >= 93 && $0 <= 100 })

print(AStudent) // [93, 94, 95, ... 100]複製程式碼

應用到投票計數

let answer = [true, false, true, false, false, false, false]

let trueAnswer = myFilter(array: answer, logic: { $0 == true })

// Trailing Closure 
let falseAnswer = myFilter(array: answer) { $0 == false }複製程式碼

Swift 裡的過濾器

幸運的是,我們不需要建立 myFilter。Swift 已經為我們提供了一個預設的。現在我們建立一個從一到一百的陣列,然後只要小於 51 的偶數。

let zeroToHund = Array(1100)
zeroToHund.filter{ $0 % 2 == 0 }.filter { $0 <= 50 })
// [2, 4, 6, 8, 10, 12, 14, ..., 50]複製程式碼

這就 OK 了。原始碼

我的訊息

我敢肯定你現在已經在想,怎麼在你的應用和程式裡使用函數語言程式設計。記住,你使用什麼語言都無所謂。

你需要清晰的是如何將函式式正規化引用到更多的領域。在你 Google 之前,我建議你花一點時間消耗一兩個腦細胞想一想。

從你理解 “filter” 背後的真正含義後,你現在可以更簡單的 google 然後檢視什麼是 mapreduce,以及其他函式是怎麼組成的。我希望能你在不冷不熱的環境中學會游泳。

你現在只被你的想象力所限制。保持思考並 Google。

最後的話

在我個人看來,這篇文章是黃金。它出現在我被閉包和函式式的東西弄得一臉懵逼的時候。人們都喜歡特別簡單的原則。如果你喜歡我的解釋,請分享並推薦給更多的人。我收到的心越多,我就會越像抽水泵一樣,為每個人獻出更偉大的內容!而且,更多的心意味著基於 Medium 演算法上的更多觀點。

有 Instagram 上的 geek 嗎?我會發布我的一些日常並更新。歡迎大家隨時新增我,跟我打招呼!@bobthedev

Swift 會議

我的一個葡萄牙朋友 João 正在葡萄牙阿威羅組織一個 Swift 會議。不像許多人在的那裡,這次會議的目的是實驗性參與。觀眾與演講者可以相互交流 - 帶上你的膝上型電腦。這是我第一次的 Swift 會議。我超興奮!除此之外,它也是經濟實惠的。活動會在 2017 年的六月一號到二號舉行。如果你有興趣瞭解更多資訊,請隨時檢視網站這裡或下面的 Twitter。

SwiftAveiro (@SwiftAveiro) | Twitter

關於我

我在我的 Facebook 頁面上給出詳細的更新資訊。一般在美國東部時間的上午八點,我會發表文章。2017 年,我立志成長為 Medium 上 iOS Geek 社群中第一的 iOS 部落格。

相關文章