iOS Tutorial – Begin with Swift ( day 5 ) – Closure

swift-128x128

รู้จักกับ closure

closure คือกลุ่มของคำสั่งที่อยู่รวมกัน สามารถจะถูกประกาศและถูกใช้ในส่วนต่างๆ ของโค้ดได้  ซึ่ง closure ใน swift เองนั้นเปรียบได้กับ block ในภาษา c และ objective-c

รูปแบบของ closure :

  • ฟังก์ชัน: ซึ่งเราพูดถึงไปในบทที่แล้ว เป็น closure ที่กลุ่มคำสั่งดังกล่าวมีการกับหนดชื่อให้กับ closure และไม่มีการบันทึกค่าต่างๆ ไว้ใช้
  • ฟังก์ชันซ้อนฟังก์ชัน: เป็น closure ที่กลุ่มคำสั่งดังกล่าวมีการกำหนดชื่อให้ และมีการบันทึกค่าต่างๆไว้ใช้
  • Closure expression: เป็นกลุ่มของคำสั่ง ที่ไม่มีการกำหนดชื่อให้กลุ่มนั้น และมีการบันทึกค่าต่างๆไว้ใช้ใน closure

โดยบทนี้เราจะมาเน้นถึง Closure Expression โดยลักษณะของการใช้ closure expression รูปแบบเต็มจะเขียนได้ดังนี้

closure

 

 

 

ชุดคำสั่งใน closure ต้องอยู่ภายในเครื่องหมาย {}  และชุดคำสั่งที่ดำเนินการภายในทั้งหมด closure จะมาหลัง keyword ‘in” โดย closure สามารถรับตัวแปรหรือไม่รับเข้ามาก็ได้ และ จะมีการคืนค่าหรือไม่มีก็ได้ตามแต่กำหนด

ใน swift นั้นมีการใช้ closure ค่อนข้างเยอะ ตัวอย่างที่เห็นได้ง่ายที่สุดและนำมายกตัวอย่างคือ ฟังก์ชัน sort(_:) ที่รับเอา closure ไปเพื่อทำการเปรียบเทียบ String 2 ตัว ว่าเรียงตามลำดับที่กำหนดหรือไม่

ตัวอย่างการใช้ฟังก์ชัน sort

func orderAsc(str1:String, str2:String)->Bool{
    return str2>str1
}
var fruitInMyBasket:Set<String> = ["banana", "mango", "apple"]
let fruitInMyBasketSort = fruitInMyBasket.sort(orderAsc)     // ทำการเรียงลำดับโดยใช้ฟังก์ชัน orderAsc

จากตัวอย่าง เราทำการกำหนดให้เมทธอด sort ซึ่งรับ closure เข้าไปเพื่อทำการเรียงลำดับข้อมูล เนื่องจากเราทราบว่า ฟังก์ชันก็เป็น closure ชนิดหนึ่งเช่นกัน เราจึงทำการประกาศฟังก์ชันชื่อว่า orderAsc ซึ่งทำการรับข้อมูลประเภท String เข้าไป 2 ตัว ซึ่งใช้ในการเปรียบเทียบกันว่าการเรียงลำดับของข้อมูลสองตัวนี้ เป็นไปตามเงื่อนไขที่กำหนดหรือไม่ ถ้าตรงตามที่เงื่อนไขกำหนดในที่นี้ก็คือ str2 จะต้องมาหลัง str1 ก็แสดงว่าเงื่อนไขเป็นจริงก็จะคืนค่า true ออกมา  ถ้าไม่เป็นไปตามเงื่อนไขที่กำหนดจะคืนค่า false จากนั้นเราทำการเรียกใช้ closure โดยส่งเข้าไปในเมทธอด sort เพื่อเรียงลำดับค่าใน array ของ String
ผลที่ได้คือ

["apple", "banana", "mango"]

เราสามารถนำฟังก์ชันด้านบนไปเขียนในรูปของ closure expression ได้ดังนี้

fruitInMyBasket.sort( { (str1:String, str2:String)->Bool in return str2>str1 } )

เนื่องจาก swift นั้นมี type inference ซึ่งจะกำหนดชนิดของข้อมูลให้อัตโนมัติ เราสามารถไม่ประกาศชนิดของข้อมูล โดยให้ swift เองกำหนดจากค่าที่ใส่ลงไป เราจึงสามารถย่อรูป closure expression โดยย่อพจน์ (str1:String, str2:String)->Bool เหลือแค่ (str1, str2)  ดังตัวอย่าง

fruitInMyBasket.sort( { (str1, str2) in return str2>str1 } )

เนื่องจากในตัวอย่าง มีชุดคำสั่งแค่บรรทัดเดียว ดังนั้นใน swift อนุญาติให้เราสามารถ ละเว้นคีย์เวิร์ด “return” ได้ และ closure จะทำการคืนค่า จากชุดคำสั่งนั้นบรรทัดเดียวนั้นโดยอัตโนมัติ เราจึงเขียน closure ใหม่ได้ดังนี้

fruitInMyBasket.sort( { (str1, str2) in str2>str1 } )

ใน swift นั้น หากเราไม่ต้องการกำหนดชื่อตัวแปรเอง เราก็สามารถใช้ shorthand argument ซึ่งจะทำการกำหนดชื่อให้ตัวแปรเราอัตโนมัติเป็น $0, $1, $2…ตามลำดับและตามจำนวนตัวแปรที่เราส่งเข้าไป ซึ่งจะสะดวกและเราสามารถเรียกใช้ตัวแปรนั้นได้ทันทีภายใน closure
นอกจากนี้เมื่อในส่วนของ closure มีแค่คำสั่งเดียวและไม่มีข้อมูลใดๆ อยู่แล้วนอกเหนือจากชุดคำสั่ง เราสามารถละเว้นคีย์เวิร์ด “in” ได้อีกด้วย ซึ่งจะทำให้เราสามารถเขียน closure ได้สั้นขึ้นอีกดังนี้

fruitInMyBasket.sort( {  $1>$0 } )

จะอ่าน closure ข้างต้นได้ก็คือ เมทธอดจะรับค่า closure ซึ่งรับตัวแปรสองตัวไปทำการเปรียบเทียบ โดยถ้าตัวที่ 2 มากกว่า(ในที่นี้คือตัวอักษรอยู่หลัง) ตัวที่ 1 ให้ส่งค่าจริง คืนกลับมา

หากเราเจอกรณีที่ ชุดคำสั่งใน statement ค่อนข้างยาว เราสามารถอาศัยคุณลักษณะอีกอย่างของ closure คือ trailing closure คือการนำกลุ่มคำสั่งทั้งหมดพร้อมเครื่องหมาย {} ออกมาอยู่นอกวงเล็บเพื่อให้กรณีที่โค้ดค่อนข้างยาวเขียนแบบนี้ทำให้การอ่านง่ายยิ่งขึ้น ตามตัวอย่าง

fruitInMyBasket.sort( ) {  $1>$0 }

จะเห็นว่า การเขียน closure expression เราสามารถจะเขียนได้หลายแบบ เราสามารถเลือกเขียนได้ตามความเหมาะสมในการเขียนโปรแกรมครั้งนั้นๆ

อ่านเพิ่มเติม ที่นี่

วันต่อไป Enumeration