[筆記] Swift深入淺出常見語法 – Functional Programming (FP)

wall of kitchen with modern furniture and appliances
分享

Swift是Functional Programming Language嗎?

剛開始理解FP時,都會想:程式碼不是都一樣,大部分程式語言都有for, if else, .map(), .filter()等判斷式與函式可以用,到底怎麼分是不是FP?網路上最多的例子就是用for 迴圈比較filter():

// 非FP
const numList = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let result = 0;
for (let i = 0; i < numList.length; i++) {
  if (numList[i] % 2 === 0) {
    result += numList[i] * 10;
  }
}

// FP
const result = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
               .filter(n => n % 2 === 0)
               .map(a => a * 10)
               .reduce((a, b) => a + b);

看完我的理解是,FP不就是用程式語言提供的函式來實現而已嗎?如果把第一個實現方法改成一個 “函式” getEvenMultipleAndSum ,不也是FP?

function getEvenMultipleAndSum(list) {
  let result = 0
  for (let i = 0; i < list.length; i++) {
    if (list[i] % 2 === 0) {
      result += list[i] * 10;
    }
  }
  return result
}

// 這樣也是FP嗎?
const result1 = getEvenMultipleAndSum([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])

// FP
const result2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
               .filter(n => n % 2 === 0)
               .map(a => a * 10)
               .reduce((a, b) => a + b);

答案:不是!(未來有機會再獨立寫一篇關於FP,順便解析為什麼 getEvenMultipleAndSum 不能算是FP)

回到開頭的問題 “Swift是Functional Programming Language嗎?“,答案: “不完全是、不是最純的FP“。那哪一個語言FP的純度最高?查了Wiki全部我都沒聽過,但常用的語言中 SQL 是最接近FP的之一。為什麼會說接近,我們來感受一個情境在兩者間不同的實現過程:請篩選出前30名年齡大於18歲的男性用戶,並按照登入時間降冪排序。

// 不使用Swift Standard Functions
var users = [...]
var result = []
for (i in 0..<users.count;) {
  if (users[i].age >= 18 && users[I].gender == 'male' ) {
    result.append(users[i])
  }
}
result = sortBySignInAt(result) // 假設已經寫好一個排序的函式
result = prefix(result, 30) // 假設已經寫好取得前幾個元素的函式
SELECT * FROM USERS 
WHERE age >= 18 AND gender = 'male' 
ORDER_BY sign_in_at DESC
LIMIT 30;

感受得出來嗎?(細節未來會獨立補充 ><)

Swift為什麼會說 “不完全是、不是最純的FP”?原因有幾個:

  • Swift提供了FP中的特定方法 (Higher-Order Functions):filter, map & reduce
  • 使用RxSwift套件,也是在Swift中實現FP的管道。
  • SwiftUI也很接近FP的概念(宣告式 Declarative Programming Style)

.map, .flapMap & .compactMap

最後也補充Swift中 .map, .flatMap & .compactMap的用法(引用Apple官方文件

let numbers = [1, 2, 3, 4]

let mapped = numbers.map { Array(repeating: $0, count: $0) }
// [[1], [2, 2], [3, 3, 3], [4, 4, 4, 4]]

let flatMapped = numbers.flatMap { Array(repeating: $0, count: $0) }
// [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]

let possibleNumbers = ["1", "2", "three", "///4///", "5"]

let mapped: [Int?] = possibleNumbers.map { str in Int(str) }
// [1, 2, nil, nil, 5]

let compactMapped: [Int] = possibleNumbers.compactMap { str in Int(str) }
// [1, 2, 5]

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *