> For the complete documentation index, see [llms.txt](https://pakornpat.gitbook.io/ios-app/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://pakornpat.gitbook.io/ios-app/v2/swift/function.md).

# Function

การประกาศฟังก์ชั่นใน Swift จะมีหน้าตาประมาณนี้

```swift
func greet(person: String) -> String {
    return "Hello, " + person + "!"
}
```

เริ่มด้วย keyword `func` จากนั้นเป็นชื่อฟังก์ชั่น ตัวอย่างนี้คือ `greet` จากนั้นจะเป็นวงเล็บ ในวงเล็บจะเป็น argument ที่จะส่งให้กับฟังก์ชั่น ถ้าฟังก์ชั่นให้ผลลัพธ์ออกมา จะตามด้วยเครื่องหมาย `->` แล้วตามด้วย Type ของผลลัพธ์จากฟังก์ชั่น&#x20;

ในฟังก์ชั่น เราใช้ keyword `return` ในการคืนผลลัพธ์ออกจากฟังก์ชั่น

วิธีการเรียกใช้&#x20;

```swift
let greetingMessage = greet(person: "pop")
```

ผลลัพธ์เก็บใน `greetingMessage` จากการเรียกฟังก์ชั่น `greet` ส่ง `"Pop"` เป็น input argument person ของฟังก์ชั่น

โดยปกติเวลาเขียน function signature แบบย่อ ๆ ของฟังก์ชั่นนี้เราจะเขียนได้ ประมาณนี้ `greet(person:)`&#x20;

ฟังก์ชั่นสามารถส่งออกผลลัพธ์ได้แค่ Type เดียว ถ้าต้องการหลายผลลัพธ์เราอาจใช้ Tuple ช่วยได้แบบนี้

```swift
func minMax(array: [Int]) -> (min: Int, max: Int) {
    var currentMin = array[0]
    var currentMax = array[0]
    for value in array[1..<array.count] {
        if value < currentMin {
            currentMin = value
        } else if value > currentMax {
            currentMax = value
        }
    }
    return (currentMin, currentMax)
}

let bounds = minMax(array: [8, -6, 2, 109, 3, 71])
print("min is \(bounds.min) and max is \(bounds.max)")
```

ฟังก์ชั่นในภาษา Swift สามารถมีชื่อตัวแปรได้สองชื่อต่อหนึ่งตัว นั่นคือ&#x20;

1. Argument label เป็น label ตอนเรียกใช้งานจากภายนอกฟังก์ชั่น
2. Parameter name ใช้งานภายในฟังก์ชั่น

(ในกรณีที่มีชื่อเดียว จะทำหน้าที่เป็นทั้ง argument label และ parameter name แบบฟังก์ชั่นที่เราเขียนกันไปแล้ว)

หน้าตาจะประมาณนี้&#x20;

```swift
func someFunction(argumentLabel parameterName: Int) {

}
```

เช่น&#x20;

```swift
func greet(person: String, from hometown: String) -> String {
    return "Hello \(person)! from \(hometown)."
}
```

จะเห็นว่า argument ที่สองของฟังก์ชั่นมี argument label ว่า `from` และมี parameter name ว่า `hometown` เวลาเรียกฟังก์ชั่นจากภายนอกจะเป็นแบบนี้

```swift
greet(person: "John", from: "Bangkok")
```

ทำให้เราอ่านแล้วลื่นไหล ทั้งจากโค้ดที่เรียกฟังก์ชั่นและโค้ดที่อยู่ในฟังก์ชั่น เพราะบริบทต่างกัน

{% hint style="info" %}
เวลาเขียน signature จะหน้าตาแบบนี้ `greet(person:from:)`&#x20;
{% endhint %}

ในบางกรณีเราต้องการให้เรียกโดยไม่ต้องระบุ label ให้เรากำหนด argument label เป็น `_` แทน เช่น

```swift
func append(_ item: String) {
}

append("Pop")
```

{% hint style="info" %}
ถึงแม้ว่าจะมี label กำกับแต่ละ argument แล้ว เวลาเรียกก็ยังต้องเรียกแต่ละ argument ตามลำดับที่ประกาศ ไม่สามารถสลับได้
{% endhint %}

เราสามารถกำหนด default value ให้กับ argument ได้โดยการกำหนดค่าในตอนประกาศฟังก์ชั่นแบบนี้

```swift
func displayItem(name: String, isDone: Bool = false) {
    print("Item name: \(name), isDone: \(isDone)")
}

displayItem(name: "Learn DDD", isDone: true)
displayItem(name: "Learn iOS")
```

จะเห็นว่าเมื่อเรากำหนด default ให้แล้ว เราสามารถละการส่ง argument นั้นได้ตอนเรียกใช้ฟังก์ชั่น

### ฟังก์ชั่นใน Swift ก็เป็น Type

```swift
func greet(person: String, from hometown: String) -> String {
    return "Hello \(person)! from \(hometown)."
}
```

ฟังก์ชั่นนี้จะมี type เป็นแบบนี้

```swift
(String, String) -> String
```

เนื่องจากฟังก์ชั่นใน Swift เป็น Type เราสามารถประกาศตัวแปรที่มี Type เป็น function ได้ เช่น

```swift
var hello: (String, String) -> String = greet

hello("Pop", "Bangkok")
```

{% hint style="info" %}
การใช้ Function Type มีข้อจำกัดอยู่ตรงที่ Function Type จะไม่สามารถมี Argument label ได้ จากตัวอย่างก่อนหน้า จะเห็นว่าเราเรียก `hello("Pop", "Bangkok")` โดยไม่มีการระบุ argument label สำหรับแต่ละ input
{% endhint %}

และด้วยความที่ฟังก์ชั่นก็เป็น Type ทำให้เราสามารถส่ง ฟังก์ชั่นเป็น input ของฟังก์ชั่นได้ด้วย เช่น

```swift
func download(url: String, completion: (String) -> ()) {
    completion(url)
}

func display(text: String) {
    print(text)
}

download(url: "mock.com", completion:display)
```

รวมทั้งคืนผลลัพธ์เป็นฟังก์ชั่นด้วย แบบนี้

```swift
func greeting(prefix: String) -> (String) -> () {
    func greet(name: String) {
        print("\(prefix) \(name)")
    }
    return greet
}

greeting(prefix: "hello")("Pop")
greeting(prefix: "hello")("John")
```

{% hint style="info" %}
เราสามารถเขียนประกาศฟังก์ชั่นในฟังก์ชั่นได้
{% endhint %}

### Closure

ถ้าอธิบายแบบง่าย ๆ คือ ฟังก์ชั่นคือ closure ที่มีชื่อ หรือก็คือ closure เป็น block ของโค้ดที่ไม่มีการประกาศชื่อนั่นเอง ตัวอย่างเช่น

```swift
func greeting(prefix: String) -> (String) -> () {
    return { name in
        print("\(prefix) \(name)")
    }
}
```

Closure คือส่วนนี้ เนื่องจาก greeting ได้ระบุ Type ของผลลัพธ์ไว้แล้วทำให้ name ใน closure สามารถ infer type เป็น String

```swift
{ name in
    print("\(prefix) \(name)")
}
```

### การลดรูปการเรียกใช้ฟังก์ชั่น ในกรณีที่ input เป็นฟังก์ชั่น

ตัวอย่างเช่น การเรียกฟังก์ชั่น index ของ array เพื่อหา index ของ item แรกที่ตรงกับเงื่อนไขที่ระบุ

ฟังก์ชั่นรับ input เป็นฟังก์ชั่นในการหา โดยวนเรียกฟังก์ชั่นนี้กับทีละ item ใน array โดยที่ถ้า item ไหนทำให้ฟังก์ชั่นที่เป็น input นี้ได้ผลลัพธ์เป็น `true` firstIndex จะให้ผลลัพธ์เป็น Index ของ item นั้น

```swift
items.firstIndex(where: { (item) -> Bool in
    item == "water"
})
```

เริ่มลดรูปจากการตัด `-> Bool` ออก เนื่องจากเรารู้จากฟังก์ชั่น firstIndex แล้วว่าต้องรับ ฟังก์ชั่นที่ return Bool

```swift
items.firstIndex(where: { (item) in
    item == "water"
})
```

ตัด (item) in ออกแทนที่ parameter แรกที่รับ ด้วย `$0`&#x20;

```swift
items.firstIndex(where: { $0 == "water" })
```

ตัด (where: ) ออก parameter สุดท้ายถ้ารับเป็นฟังก์ชั่น สามารถละได้

```swift
items.firstIndex { $0 == "water" }
```
