忘備録-Swiftの関数

趣味でIOSアプリ開発をかじっていた自分が、改めてSwiftを勉強し始めた際に、曖昧に理解していたところや知らなかったところをメモしています。

## 参考文献

この記事は以下の書籍の情報を参考にして執筆しました。

- [詳解 Swift 第5版](https://www.amazon.co.jp/%E8%A9%B3%E8%A7%A3Swift-%E7%AC%AC5%E7%89%88-%E8%8D%BB%E5%8E%9F-%E5%89%9B%E5%BF%97/dp/481560407X)

##関数の概要
func 関数名(仮引数: 型) ->  型{
  文…
}
関数内の処理で呼び出し元から値を使わない場合、「仮引数: 型」は記述しない。
関数から戻り値を返さない場合、「 ->  型」は記述しない。
##returnの省略
関数本体がreturn１行だけの時はreturnを省略することができる。

```swift
func add(a: Int, b: Int) -> Int{
  return a+b
}
print(add(a:3, b:5))		//8
```

```swift
func add(a: Int, b: Int) -> Int{
  a+b
}
print(add(a:3, b:5))		//8
```

##引数ラベルの指定
func 関数名(引数ラベル名　仮引数: 型)で引数ラベルを指定することができる。

```swift
func area(height h: Int, weight w: Int) -> Int{
  h * w
}
print(area(height: 3, weight: 5))    //15
```

##引数ラベルの省略
引数ラベルの代わりに「 _ 」を使用することで引数ラベルを省略できる。

```swift
func add(_ a: Int, _ b: Int) -> Int{
  a+b
}
print(add(3, 5))    //8
```

##inout引数
SwiftにはC言語のようなポインタ変数がない。代わりにinout引数を使うことで、呼び出し側の変数を変更できる。

```swift
func swap(_ a: inout Int, _ b: inout Int) {
  let t = a; a = b; b = t
}
```

inout引数を使用して関数を呼び出す際は「 & 」をつけて呼び出しを行う。

```swift
var x = 10
var y = 3
print(swap(&x, &y))
print("x = \(x) , y = \(y)")        //x = 3 , y = 10
```

##引数ラベルを使ったオーバーロードの定義
引数ラベルで異なる引数を定義することで同名で別の関数を定義することができる。

```swift
func swap(_ a: inout Int, _ b: inout Int) {
  let t = a; a = b; b = t
}
func swap(little a: inout Int, great b: inout Int) {
  if b > a{
    let t = a; a = b; b = t
  }
}
var x = 10
var y = 3
print(swap(&x, &y))
print("x = \(x) , y = \(y)")
print(swap(little: &x, great: &y))		//x = 3 , y = 10
print("x = \(x) , y = \(y)")		//x = 10 , y = 3
```

##関数の引数に規定値を設定する
仮引数に規定値を指定することができる。規定値を指定する場合、呼び出し時に省略することが可能。

```swift
func setSeparator(color: String = "Black", width: CGFloat){
    //線を追加する処理
}
//色はデフォルト値でいい
setSeparator(width: 200)
//色は赤がいい
setSeparator(color:"red", width: 200)
```

##返り値を使わない場合を許す指定
次のようにBool型を返す関数があったとしても通常返り値を使いたくないのであれば関数定義の直前に@discardableResultをつけることでコンパイルできるようになり、帰り値を使わなくてもエラーにならない。

```swift
@discardableResult func hello(name: String) -> Bool{
    var b: Bool = false
    if name == ""{
      print("Hello NANASHIsan")
      b = false
    } else {
      print("Hello\(name)san")
      b = true

    }
    return b
  }
hello("hoge")
```

##キーワード付きのタプル
タプル : 複数のデータを組みにしてまとめたもの。
要素を取り出す時は番号で位置を指名することができる。
タプルは同じ型を持つ変数に代入できる。

```swift
let book = ("hoge hoge book", 276)
print("\(book.0)のページ数\(book.1)")
let (title, page) = book
print("\(title)のページ数\(page)")
```

一部変数だけ取得することも可能

```swift
let book = ("hoge hoge book", 276)
let (title, _) = book
print("\(title)読んだ")
```

要素を取り出す時にキーワード指定で取り出すことができる。
キーワードのついたタプルは同じキーワードがついたタプルかキーワード指定のないタプル間でしか代入できない。
ただしキャストするとキーワード情報がリセットされるので代入できるようになる。

```swift
let book = (title: "hoge hoge book", page: 276)
print("\(book.title)のページ数\(book.page)")
let v1: (String, Int) = book
let v2: (title: String, page: Int) = book
let v3: (bookName: String, count: Int) = book 	//エラー
let v4: (bookName: String, count: Int) = book as(String, Int)
```

##演算子の定義
概要
・二項演算子
infix operator 演算子 : 優先度グループ名(省略可能)
・前置演算子
prefix operator 演算子
・後置演算子
postfix operator 演算子

Swiftでは自分で演算子を定義することができる。ただ、どのような演算子を定義するかによって定義方法が異なる。
(1)全く新しい演算子の定義。
(2)既存の演算子に別の使い方を定義。
(a)これまで単項演算子として使用されていたものに、二項演算氏の役割を定義。またはその逆。
(b)これまでとは違うデータ型(の組み合わせ)に対して適応可能にする。

(1),(2)-(a)の場合、演算子として使う記号と使い方(前置演算子/後置演算子/二項演算子)を定義する必要がある。
この宣言はどの関数の定義にも属さずトップレベルで行う必要がある。
(2)-(b)の場合、改めて宣言する必要がない。

二項演算子の定義

```swift
infix operator ☆ : RangeFormationPrecedence

func ☆(a: Int,b: Int) -> String {
  String(a) + String(b)
}
print(20☆19)    //2019
```

単項演算子の定義(前置演算子、後置演算子)

```swift
postfix operator %    //後置演算子を定義

postfix func % (n: Double) -> Double {
  n * 0.01
}
print(10250%)    //102.5
```

####演算子として独自定義できない文字列
= , -> , . , // , /* , */ , ? ,
前置演算子として定義できない。
& , >
後置演算子として定義できない。
 <  , !
####演算子として使える文字列
下記のASCEII文字列の記号
/ ,  - , + , ! , * , % ,  , | , ^ , ~
Unicode文字列の記号
→ ,  Δ , ★ , (etc…)
組み合わせとして使用できる記号
= , ? ,  ! , > , < , &
これらを１つまたは複数組み合わせて定義する。
また「.」から始まる場合のみ、「.」を使用可能。
優先度グループの定義
##優先度グループの定義
優先度 : １つの式に異なる演算子が複数あった場合に、どちらが先に解釈されるかを示します。
(記号例は一部抜粋。他にもあるので注意。)

|グループ名|記号例|
|------------------------|---|
BitwiseShiftPrecedence|<< , &>>
MultiplicationPrecedence|* , /
AdditionPrecedence| + , - , ^
RangeFormationPrecedence|..<
CastingPrecedence|is , as
NilCoalescingPrecedence|??
ComparisonPrecedence|< , >=
LogicalConjunctionPrecedence| &&
LogicalDisjunctionPrecedence| ｜｜
TernaryPrecedence| con , ?
AssignmentPrecedence| = , += , %=

演算子の優先度を独自に定義することもできる。

```swift
precedencegroup HogeGroup {
    associativity: none    //結合規則
    higherThan: LogicalDisjunctionPrecedence    //優先度
    lowerThan: LogicalConjunctionPrecedence    //優先度
    assignment: false    //代入演算子
}
infix operator .%% : HogeGroup
func .%% (a: Double , b: Double) -> String {
    String((a+b)*0.1)
}
print(3000 .%% 4400)
```