Swiftの関数 | 戻り値(Return Values)

Swiftの関数 | 戻り値(Return Values)

関数の戻り値(Return Values)です。戻り値とは、関数の出力(アウトプット)のことです。ここでは、先ずreturn制御構文を簡単に説明してから、戻り値のない関数と戻り値のある関数の比較、さらに戻り値がOptionals(正確にはoptional tuples)な関数について説明します。

戻り値(Return Values)は関数のアウトプット

戻り値または返り値(Return Values)とは、その名前の通り、関数が返す(return)値(value)のことです。つまり、戻り値は出力(アウトプット)です。

値を返すので、必ず型を指定することになります。この後詳しく見ていきますが、複数の戻り値を持たせることも可能で、tupleを使って実装します。また、戻り値がない関数、というのを定義することも可能です。

return制御

return制御は関数やメソッド内部で使える制御転送です。returnが実行されると、直ちに関数(またはメソッド)を抜けます。

例えば、(実用性は全く無いですが)以下のような関数では、

// returnで直ちに関数を抜ける
func someFunction() -> Int {
    return 1
    // returnで関数を抜けるので、return以降は実行されない

    print("Hello!")
}

return 1の後のprint()が呼び出されることはありません。

また、returnで返す値は、戻り値で指定されている型です。上記の場合ですと整数型Intでなければいけません。したがって、例えば上記の関数でreturn 1.0と書くと

//error: cannot convert return expression of type 'Double' to return type 'Int'

と怒られます。

後で紹介しますが、戻り値のない関数というのを定義することも可能です。そういう関数でもreturnを使用することが可能で、例えば

func someFunction() {
    print("Hello")
    return
}

と、明示的にreturnを書いても構いません。

戻り値の定義構文 | 戻り値が1つだけの場合

関数に戻り値を指定する場合には

// 戻り値の定義構文
func functionName() -> returnType {
  return value
}

のように、パラメータを定義するカッコ()の後に、矢印と型-> returnTypeを指定します。先程も述べましたが、valueは型に適合した値でないとコンパイルエラーになります。例えば、文字列型を返したい場合は-> Stringと書きます。

関数の引数のページで紹介したhelloWorld()は、戻り値が1つだけの関数になります(引数はなし)。

// 引数なしの関数
func helloWorld() -> String {
    return "ハローワールド!"
}
print(helloWorld())
//"ハローワールド!"と表示

戻り値のない関数

関数のページの最初の方で

関数の定義は「関数がどんな処理をして、どんなパラメータを受け取り、どんな戻り値を返すか」をユーザに示すものです。

と言いましたが、実は戻り値も必須ではありません。

// 戻り値のない関数
func hello() {
    print("こんにちわ")
}
hello()
//"こんにちわ"と表示

戻り値を指定しない場合は-> returnTypeを省略します。この関数は戻り値もパラメータもありませんので、もっとも単純な関数の形になります。

代入演算子には戻り値がない」で触れましたが、戻り値のない関数というのは、実際はVoidという型が割り当てられています。

複数の戻り値を持つ関数(戻り値がtupleの関数)

関数に戻り値が複数欲しい場合はtuplesが使えます。Tuplesは手軽に使用できるので、この実装は非常にありがたいです。公式マニュアルの例にあるminMax()関数が分かりやすいので、ここではそれを流用します。

// 複数の戻り値を持つ関数
func minMax(array: [Int]) -> (Int, Int)  {
    var currentMin = array[0]
    var currentMax = array[0]
    for value in array[1.. currentMax {
            currentMax = value
        }
    }
 
    return (currentMin, currentMax)
}

let bounds = minMax(array: [8, -6, 2, 109, 3, 71])
print("最小値は\(bounds.0)、最大値は\(bounds.1)")
//"最小値は-6、最大値は109"と表示

確かに、入力した配列から最小と最大の要素を取り出せていることが分かります。少し複雑なので、順番に見ていきます。

Arrayを引数に、tupleを戻り値に指定

関数の定義を見てみると

// 関数の定義
func minMax(array: [Int]) -> (Int, Int)  {
    ....
    return (currentMin, currentMax)
}

となっています。今、引数は(array: [Int])となっています。これは

引数を配列リテラル[Int]で指定して、その名前をarrayとする

ということです。

また戻り値の型は(Int, Int)ですから、2つの整数型をもつtupleです。したがって、関数の戻り値自体も同じtupleでないといけませんからreturn (currentMin, currentMax)となっています。

Array要素から最小値と最大値を見つける

関数の本体は、最小値と最大値を見つける部分です。

// 最小値と最大値
....
    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
        }
    }
....

まずArrayの最初の要素array[0]を変数に代入します。これが後で比較する際の基準になります。

次に、for-inループを使ってArray要素を全てチェックしていきます。

for value in array[1..<array.count] {

この時、範囲演算子を使って、2番目から最後の要素までをループで回しています。最初の要素は基準となる値なので、ループに含める必要はありません。

値の比較をしている部分は簡単だと思います。else ifを使っている理由は、value = currentMin (or currentMax)となる場合を排除したいためです。

minMax()関数の使用

実際に関数を呼び出す場合は、例のようにArrayリテラルを入れてもいいですし、先に配列を宣言してから入れても問題ありません。

// 配列をパラメータに
let someArray: [Int] = [-2, -1, 0, 1, 2]
let boundsSomeArray = minMax(array: someArray)
print("最小値は\(boundsSomeArray.0)、最大値は\(boundsSomeArray.1)")
//"最小値は-2、最大値は2"と表示

戻り値がoptional tupleな関数

先ほど定義したminMax()関数は、実は少し問題があります。というのは、「もし配列が空の場合はどうする?」というチェックが欠けているからです。実際に空の配列リテラルを入れてみると実行エラーを起こします。

// 空の配列を入れる
let boundsEmptyArray = minMax([])
// 実行エラーでクラッシュ

こういう時はオプショナル型(optionals)の出番です。

今戻り値はtupleですから、tuple全体をオプショナル型に指定すれば良いです。また、パラメータである配列が空の場合にはnilを返すというチェックも入れてみましょう。

// 複数の戻り値を持つ関数、optionalsを戻り値にする
func minMax(array: [Int]) -> (min:Int, max:Int)?  {
    if array.isEmpty {
        print("配列が空です。最小値、最大値を計算できません")
        return nil
    }
    
    var currentMin = array[0]
    var currentMax = array[0]
    for value in array[1.. currentMax {
            currentMax = value
        }
    }
    
    return (currentMin, currentMax)
}

let boundsEmptyArray = minMax(array: []) // 空の配列を入れる
if let bounds = minMax(array: [8, -6, 2, 109, 3, 71]) {
    print("最小値は\(bounds.min)、最大値は\(bounds.max)")
}
//"最小値は-6、最大値は109"と表示

こうすると、配列が空の場合でもエラーを起こさなくなりました。

今、戻り値全体をオプショナル型にしたいので(min:Int, max:Int)?と、tuple全体をオプショナル型にしました。また、tupleの要素に名前を付けましたので、値を参照する時にこの名前を使うことができます。

アウトプットを取り出す時にはOptional Bindingなどを使います。

if let bounds = minMax(array: [8, -6, 2, 109, 3, 71]) {
    print("最小値は\(bounds.min)、最大値は\(bounds.max)")
}
//"最小値は-6、最大値は109"と表示

まとめ

  • 関数の戻り値は、->returnTypeで指定(戻り値が1つの場合)
  • 関数の戻り値は必須ではない。戻り値のない関数を定義することも可能
  • 複数の戻り値を関数に持たせる際には、tupleを戻り値にする