Hello Swifty, ฉลองบทความแรกของ swift 🎉 มาเขียนภาษาที่ผมถนัดสะที 😂 ไหนๆเริ่มต้น ขอเริ่มด้วย HOF (higher order function) เลยละกัน
ในภาษา swift เราใช้ function กันเป็นว่าเล่น วันนี้เราเลยลองมาใช้ function ที่มัน advance ขึ้นมาอีกนิ้ดนึง
หลายคนอาจจะร้องอ๋อ เพราะเคยใช้มาแล้วแต่อาจจะไม่รู้ว่ามันคือ HOF ส่วนคนที่ไม่เคยใช้ ก็ไม่ต้องใช้หรอกคับ ผ่าม! ได้ใช้บ่อยแน่นอนไม่ต้องห่วงง 55
What is Higher-Order Function
ได้ยินคำว่า higher แปลว่า function นี้มันอยู่สูงจริงมั้ย ผมตอบเลยว่าจริง !
เพราะว่าผมวางโค้ดให้อยู่บรรทัดแรกๆ .. 🐸
เอาใหม่ ได้ยินแล้วอาจจะ งงๆ แต่ไม่ต้อง งง จริงๆแล้วมันแค่นี้เอง มันคือ function ที่
- รับ parameter เป็น function หรือ closure
- return ออกโดยใช้ function ที่เรารับเข้ามา
พูดง่ายๆคือมันเป็น function ที่ทำหน้าที่ส่งต่อให้อีก function ทำต่ออีกที แค่นี้เอ๊งง

(T คือ Generic แปลว่ามันจะเป็น type อะไรก็ได้)
ข้อดี
- flexible (ลองดูตัว custom function ข้างล่าง)
- clean เพราะมันเขียนสั้นลงมากก
- ประหยัดเวลา swift มี buit-in functions ให้ใช้เพียบ
ตัว Higher order function มันเป็นหัวใจหลักของ functional programming เลย (คือแนวคิด การเขียน program โดยใช้ function เป็นหลัก)
ซึ่ง swift เนี่ยภาษามันเป็น multi paradigm แปลว่าใช้ได้ทั้ง OOP, functional อย่างเฟี้ยว เวลาทำงานจริงเราใช้ผสมกันเป็นโกโก้เลย 55
Swift Built-In Higher-Order Functions
ปูมาขนาดนี้เพราะภาษา swift มีมาใช้เพียบเลย บทความนี้บีมจะมาแนะนำ 10 อันที่ใช้กันบ่อยๆ และ อันที่น่าใช้ (ก่อนเรียกใช้อย่าลืมม import foundation นะค้าบบ 😂)
- map
- compactMap
- flatMap
- reduce
- sorted
- filter
- contains
- allSatisfy
- first(where)
- dropWhile
Before we start ต้องเข้าใจ $0 ก่อน
ไม่ต้องห่วงเห็นครั้งแรก งง ทุกคน $0 คือตัวแทน parameter ตัวแรกใน closure, function ซึ่งของใน built-in ส่วนใหญ่ มันก็คือตัวแทน ของค่าของ index ที่มันโดนวนอยู่ – ถ้ามีหลาย parameter ก็จะใช้ $1, $2 ไปเรื่อยๆ
ตัวอย่าง
let result = [1, 2, 3, 4].map { number in
return number * 2
}
อย่างแบบนี้ $0 ก็คือ number นั่นแหละ เขียนได้เหมือนกันทั้งสองแบบ ถ้าเข้าใจแล้วก็ลุยกันเลย
let result = [1, 2, 3, 4].map { $0 * 2 }
Map
- เป็นการวนใน collection ของเรา เพื่อให้เราเอาไปทำอะไรสักอย่างต่อ
- เหมาะเอาไปใช้แปลงข้อมูล จากของเดิม
- จะได้ result เป็น array
ตัวอย่าง:
- ยกกำลัง วนแล้วคูณเลขตัวเอง (square root)
let squareRoot: [Int] = [1, 2, 3, 4]
let toPrintSquareRoot: [Int] = squareRoot.map { $0 * $0 }
// output: [1, 4, 9, 16]
CompactMap
- เหมือน map ทุกอย่างแต่ไม่เอา result ที่ได้ nil ❌
- เหมาะเอาไปใช้แปลงข้อมูล ที่ไม่มี nil
- จะได้ result เป็น array
ตัวอย่าง:
- วนแล้วเปลี่ยน string ตัวเลขทุกตัวเป็น type int
- “impostor” เปลี่ยนเป็น type int ไม่ได้เลยได้ nil
let numberWithNil: [Int?] = [
"1",
"2",
"impostor",
"3"
]
let toPrintCompactMap: [String] = numberWithNil.compactMap { Int($0) }
// output: [1, 2, 3]
FlatMap
- เอาไว้ลบ nested array
[[1, 2, 3]] -> [1, 2, 3] - จะได้ result เป็น array
- ลบ array ได้ทีละชั้น !
- ลบได้มากสุด ยังไงก็จะเหลือ array 1 ชั้นเสมอ !!
ตัวอย่าง 1 (one layer nested array):
- ใช้ flatMap ลบ array ชั้นนอกสุดออก
let twoLayerNestedArray: [[[String]]] = [
["1"],
["2"],
["3"]
]
let simpleArray: [String] = twoLayerNestedArray.flatMap { $0 }
// output: ["1", "2", "3"]
ตัวอย่าง 2 (two layer nested array):
- flatMap มันลบได้ทีละชั้น
- ถ้าจะลบ 3 ชั้นก็ต้องใช้ flatMap อีกครั้ง 😎
let threeLayerNestedArray: [[[String]]] = [[["1"], ["2"], ["3"]]]
let simpleArray: [String] = threeLayerNestedArray.flatMap { $0 }.flatMap { $0 }
// output: ["1", "2", "3"]
Reduce
- จำว่า reduce คือการ ทำให้ array ลด(รวม) เหลือแค่ค่าเดียว
- parameter ของ
reduce(initialValue)ข้างในวงเล็บอะ คือค่าตั้งต้น - type ของ result คือ type เรา set ไว้อยู่แล้วตอนแรก เช่น
reduce(0)เราจะได้ค่า int
ตัวอย่าง:
- process ของมันเริ่มจาก เอา initialValue ที่ตั้งไว้คือ 0 มาบวก 1 (array index แรก) ก็จะได้ 1
- เสร็จแล้ว ตัวต่อไปคือ 2 มาบวกกับ 1 ตัวก่อน จะได้ 3
- ทำแบบนี้ต่อไปเรื่อยๆจนครบใน array
- ใน reduce $0 คือค่าที่ถูกเก็บไว้ $1 คือค่าของ index ปัจจุบัน
let numbers: [Int] = [1, 2, 3, 4]
let sumNumber: Int = numbers.reduce(0) { $0 + $1 }
// output: 10
Sorted
- เอาไว้เรียงค่าตามตามที่เราตั้ง
- จะได้ result เป็น array
ตัวอย่าง:
- เรียงจากน้อยไปมากเพราะใช้ <
- จะเรียงแบบอื่นก็เปลี่ยนตอนดัก
let unorderedNumbers: [Int] = [44, 55, 22, 33]
let orderNumbers: [Int] = unorderedNumbers.sorted { $0 < $1 }
// output: [22, 33, 44, 55]
Filter
- เอาไว้กรอง
- อันนี้ใช้บ่อยมากก ส่วนใหญ่เราต้องคัดของใน array ตลอดอยู่แล้ว
- จะได้ result เป็น array
ตัวอย่าง:
- กรองเอาเลขคู่ (ตัวที่หาร 2 ลงตัว)
let numbers: [Int] = [1, 2, 3, 4, 5, 6]
let evenNumbers: [Int] = numbers.filter { $0 % 2 == 0 }
// output: [2, 4, 6]
Contains
- เช็คใน collection ว่ามี condition ที่ match มั้ย ถ้ามีจะได้ true ทันที
- เหมาะเอาไว้เช็คว่าข้างในมีของที่เราหามั้ย
- จะได้ result เป็น boolean
ตัวอย่าง:
- เช็คว่าคะแนนสอบของเด็กมีใครได้ F มั้ย 🥲
let studentsGrades: [String] = [
"A",
"B",
"C",
"D",
"E",
"F"
]
let isContainFailStudent: Bool = studentsGrade.contains { $0 == "F" }
// output: true
AllSatisfy
- จะได้ result เป็น boolean
- เช็คว่าใน collection match กับ condition ของเราทั้งหมดมั้ย
- ถ้าเช็คแล้วเป็น true หมด result ก็จะได้ true
ตัวอย่าง:
- เช็คว่าใน array เป็นเลขคู่ทั้งหมดมั้ย
let numbers: [Int] = [2, 4, 6]
let isAllEven: Bool = evenNumbers.allSatisfy { $0 % 2 == 0 }
// output: true
First (where)
- เอาไว้หาตัวที่ตรงกับเงื่อนไขเราตัวแรก
- ได้ result type เดียวกับ type ข้างใน array
ตัวอย่าง:
- หาผลไม้ชื่อแรกใน array ที่ขึ้นต้นด้วย B
- ในนี้มีทั้ง Banana กับ Blueberry ที่ขึ้นต้นด้วย B แต่เราจะได้ Banana 🍌 เพราะลำดับมาก่อน
let fruits: [String] = [
"Apple 🍎",
"Banana 🍌",
"Cherry🍒",
"Blueberry 🫐",
"Mango 🥭"
]
let letterBfruit: String = fruits.first(
where: { $0.hasPrefix("B") }
) ?? "No fruit found! 🍏"
// output: Banana 🍌
DropWhile
- จำว่า drop คือลบออก
- เอาไว้ลบทุกตัว ก่อนที่จะ match กับ condition
- หลังจากมันเจอ condition ที่ได้ true แล้วมันถึงหยุด
ตัวอย่าง:
- รวมคะแนนของนักเรียน ที่ได้มากกว่า 50 🤓
- ใช้ dropWhile อันไหนน้อยกว่า 50 ก็โดนลบออกหมด
let scores: [Int] = [30, 40, 45, 50, 60, 40, 70, 80]
let filteredScores: [Int] = scores.drop(while: { $0 < 50 })
// output: [50, 60, 40, 70, 80]
Custom Higher-Order Function
นอกจากที่เค้าให้มา เรามาสร้างเองก็ทำได้ชิวๆ
ตัวอย่าง:
- ฟังชั่น modifyTextInput เอาไว้แปลง string text ที่ส่งเข้าไป
- customOperation คือ ฟังชั่นที่เรา เอาไว้สั่งให้ text มันเปลี่ยนเป็นอะไร
- customOperation รับ function หรือ closure type (string) -> (string) เท่านั้น
// Higher-Order Function ตัวตั้งต้นของเรา
func modifyTextInput(text: String, using customOperation: (String) -> String) -> String {
return customOperation(text)
}
Function
สังเกตดูว่ามัน dynamic มาก เพราะจะเอา function อันไหนไปใช้ก็ได้ อยากใช้แบบอื่นก็สร้างเพิ่มได้เรื่อยๆ
อะเข้า concept SOLID ละ 1 จุด Open-Closed Principle 🤫
ในนี้ผมสร้างมา 3 function เอาไปใช้ใน modifyTextInput ได้หมดเลย ผลลัพท์ก็ได้ตามใน output คับ
1.
func trimText(text: String) -> String {
return text.trimmingCharacters(in: .whitespacesAndNewlines)
}
let trimmedText: String = modifyTextInput(
text: " Hello World ",
using: trimText
)
// output: "Hello World"
2.
func replaceSpaceWithDash(text: String) -> String {
return text.replacingOccurrences(of: " ", with: "-")
}
let replacedText: String = modifyTextInput(
text: "Hello World",
using: replaceSpaceWithDash
)
// output: "Hello-World"
3.
func addHapiness(text: String) -> String {
return "🥳\(text)🥳"
}
let happyText: String = modifyTextInput(
text: "Hello World",
using: addHapiness
)
// output: "🥳Hello World🥳"
Closure
ท่าที่ใช้ closure ก็คล้ายๆ function แค่เราไม่ต้องสร้าง function เพิ่ม
1.
let trimmedText = modifyTextInput(
text: " Hello World ",
using: { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
)
// output: "Hello World"
2.
let replacedText = modifyTextInput(
text: "Hello World",
using: { $0.replacingOccurrences(of: " ", with: "-") }
)
// output: "Hello-World"
3.
let happyText = modifyTextInput(
text: "Hello World",
using: { "🥳\($0)🥳" }
)
// output: "🥳Hello World🥳"
Tip: จริงๆถ้า parameter ตัวสุดท้ายของ function หรือเป็น closure เราไม่ต้องเขียน ชื่อ parameter ตอนเรียกใช้ก็ได้ เช่น ข้างบนถ้าจะตัด using ไปเลยก็ได้ แล้วต่อด้วย block code เหมือนเดิม แต่ในนี้บีมจะยังใส่ไว้ให้เข้าใจได้ง่ายๆค้าบ
จบแล้วลองเอาไปฝึกใช้ดูนะครับ ใช้คล่องๆได้นี่เก่งมากเลย ประหยัดเวลา dev แถมอ่านง่ายทีม review code สบาย ใครสนใจไปดูเพิ่มได้ใน
- Apple Array Document มีให้เล่นอีกเยอะเลย
แต่ขอเตือนว่า ระวังอย่าไปใช้ตอนสอบสัมภาษณ์งานนะคร้าบบ เพราะมันเขียนสั้นโกงเกินไปปไม่ดีๆ 555 😂
Happy Coding Swift คร้าบบ


Leave a Reply