Swift 6.0 beta 日本語化計画 : Swift 6.0 beta
関数(Functions)
関数 は、特定のタスクを実行する自己完結型のコードの塊です。それが何をするか識別するため関数に名前を与え、この名前は必要なときにそのタスクを実行する関数を "呼び出す" ために使用されます。
Swift の統一関数の構文は、パラメータ名のない単純な C スタイルの関数から、各パラメータに名前と引数のラベルを付けた複雑な Objective-C スタイルのメソッドまで、全ての物を表現するのに十分な柔軟性があります。パラメータは、関数呼び出しを簡素化するためにデフォルト値を提供することができ、入出力のパラメータとして渡すことができ、関数がその実行を一旦完了すると渡された変数を変更します。
Swift 内のすべての関数には、関数のパラメータ型と戻り値の型からなる型があります。それが簡単に他の関数へのパラメータとして関数を渡す事ができ、関数から関数を返せるので Swift の他の型のように、この型を使用できます。入れ子にされた関数の範囲内で便利な機能をカプセル化するために、他の関数内で関数をもまた記述することができます。
あなたが関数を定義するときは、必要に応じて パラメータ として知られる、入力として関数が取る、1つ以上の、名前を付けた型付きの値を定義できます。また、必要に応じて、それが行われた時、その 戻り値の型 として知られる、関数が出力として渡す値の型を定義できます。
すべての関数は、関数が実行するタスクを記述する 関数名 を持っています。関数を使用するには、その名前でその関数を "呼び出し"、関数のパラメータの型と一致する入力値、(引数 として知られています)を渡します。関数の引数は、常に、関数のパラメータリストと同じ順序で提供されなければなりません。
以下の例の関数は、greet(person:) と呼ばれ、というのもそれが行う事だからですが、入力として人の名前を取り、その人のための挨拶を返します。これを実現するために、一つの入力パラメータである、 person と呼ばれる String 値を定義し、その人のための挨拶を含むことになる String の戻り値の型を定義します。
この情報はすべて、func キーワードが接頭辞として付いている関数の 定義 に巻き込まれています。
戻り矢印 -> (右山括弧が続くハイフン) で、返すべき型の名前が続き、関数の戻り値の型を示しています。
定義は、関数がする事、関数が受け取るように期待するもの、そしてそれが行われたときに返すものを説明します。コードの他の場所から明確に呼び出せるように関数を定義すると、それが容易になります:
greet(person: "Anna") のような、person 引数ラベルの後に String 値をそれに渡すことで greet(person:) 関数を呼び出して下さい。関数は、String 型の値を返しますので、上記のように、その文字列を印刷し、その戻り値を見るには print(_:separator:terminator:) 関数への呼び出しの中に greet(person:) は包み込まれています。
greet(person:) 関数の本体は、greeting と呼ばれる新しい String 定数を定義し、簡単な挨拶をそれに設定することで始まります。この挨拶は、その後 return キーワードを使用して関数の外に渡されます。return greeting というコードの行では、この関数はその実行を終了し、greeting の現在の値を返します。
異なる入力値で greet(person:) 関数を複数回呼び出すことができます。上記の例では、"Anna" の入力値、および "Brian" の入力値で呼び出された場合に何が起きるかを示しています。関数は、それぞれの場合にぴったりとした挨拶を返します。
この関数の本体を簡素化して、メッセージの作成と return 文を組み合わせて1行にします:
関数のパラメータと戻り値は、Swift では非常に柔軟です。一つの名前のないパラメータを持つ簡単なユーティリティ関数から表情豊かなパラメータ名と、様々なパラメータオプションを持つ複雑な関数まで何でも定義することができます。
関数は、入力パラメータを定義する必要はありません。ここでは、それが呼ばれるたびに常に同じ String メッセージを返す、入力パラメータのない関数の例を挙げます。
関数の定義はそれが全くパラメータを取らないにもかかわらず、関数名の後の括弧をまだ必要とします。関数が呼び出されたときに、関数名にも括弧の空のペアが続きます。
関数は、関数の括弧内にカンマで区切って書かれた、複数の入力パラメータを持つことができます。
以下の関数は、人の名前と、彼らがすでに挨拶されたかを入力として取り、そしてその人のための適切な挨拶を返します。
greet(person:alreadyGreeted:) 関数を呼び出すのに person とラベルされた String 型の引数値と alreadyGreeted とラベルされた Bool 引数値の両方をカンマで区切って括弧内に入れ渡します。以前のセクションで示した greet(person:) 関数とはこの関数は異なることに注意してください。両方の関数とも greet で始まる名前を持っており、greet(person:alreadyGreeted:) 関数は 2 つの引数を取りますが、greet(person:) 関数は一つだけを取ります。
関数は戻り値型を定義する必要はありません。ここでは greet(person:) 関数の一つのバージョンを示しますが、それは値を返すよりも、独自の String 値を印刷します。
戻り値を返す必要はないので、関数の定義は、戻り矢印 (->) や戻り値の型を含んでいません。
関数が呼び出されたときにその戻り値は無視することができます:
最初の関数、printAndCount(string:) は、文字列を印刷し、その後 Int としてその文字数を返します。第二の関数、printWithoutCounting(string:) は、最初の関数を呼び出しますが、その戻り値を無視します。第二の関数が呼び出されると、メッセージがまず最初の関数により印刷されますが、戻り値は使用されません。
1 つの複合した戻り値の一部として複数の値を返すように関数の戻り値の型としてタプル型を使用できます。
以下の例では、Int 値の配列の最小と最大の数字を見つける、minMax(array:) という関数を定義しています。
minMax(array:) 関数は、2つの Int 値を含むタプルを返します。関数の戻り値を照会するとき、名前でアクセスできるように、これらの値は、min と max のラベルが付いています。
minMax(array:) 関数の本体は、配列内の最初の整数の値に currentMin と currentMax という2つの作業変数を設定することによって開始します。その後関数は、残りの配列の値がそれぞれ currentMin と currentMax の値より小さいか大きいかを繰り返し確認します。最後に、全体の最小値と最大値を、2つの Int 値のタプルとして返します。
タプルのメンバーの値が関数の戻り値の型の一部として命名されているので、それらは最小値と最大値に基づいた値を取得するために、ドット構文を使用してアクセスできます:
自分の名前が既に関数の戻り値の型の一部として指定されているため、タプルのメンバーは、タプルが関数から返された時点では名前を付ける必要はないことに注意してください。
関数から返されるタプル型が全体のタプルで "値がない" 可能性がある場合は、全体のタプルが nil になりうるという事実を反映して、optional のタプル型の戻り値を使用できます。(Int, Int)? や (String, Int, Bool)? のように、タプル型の閉じ括弧の後に疑問符を書くことによって、optional のタプルの戻り値の型を書いて下さい。
上記の minMax(array:) 関数は、二つの Int 値を含むタプルを返します。しかし、関数は、それが渡される配列上の安全性チェックを何ら実行しません。array の引数が空の配列、つまり、上記で定義された minMax(array:) 関数を含む場合は、array[0] にアクセスしようとしたとき、実行時エラーを引き起こします。
この"空の配列"を安全に処理するには、optional のタプルの戻り値型を持つ minMax(array:) 関数を記述し、配列が空の場合に、nil の値を返すようにします:
minMax(array:) 関数のこのバージョンが、実際のタプル値または nil を返すかどうかを確認するために、optional の結合を使用できます。
関数の本体全体が単一の式である場合、関数はその式を暗黙的に返します。たとえば、以下の両方の関数の動作は同じです。
greeting(for:) 関数の完全な定義は、それが返す挨拶メッセージです。つまり、この短い形式を使用できる事を意味します。anotherGreeting(for:) 関数は、長い関数のように return キーワードを使用して、同じ挨拶メッセージを返します。ただ 1 つの return 行として記述する全ての関数は、return を省略できます。
ゲッタ宣言の省略形 で見るように、プロパティ getter は暗黙的な return を使用することもできます。
各関数のパラメータは、引数のラベル と パラメータ名 の両方を持っています。引数のラベルは関数を呼び出すときに使用されます。各引数は、関数呼び出しでその前にその引数のラベルが書かれます。パラメータ名は、関数の実装に使用されます。デフォルトでは、パラメータは、それらの引数のラベルとして、それらのパラメータ名を使用します。
すべてのパラメータは、独自の名前を持っていなければなりません。複数のパラメータは、同じ引数ラベルを持つことが可能ですが、独自の引数ラベルは、あなたのコードを読みやすくするのに役立ちます。
パラメータ名の前に引数のラベルを、スペースで区切って書いて下さい。
ここで、人の名前と出身地を取り、挨拶を返す greet(person:) 関数のバリエーションを挙げましょう。
引数のラベルの使用は、関数が表現力豊かで、文章のような方法で、呼び出すことができるようにし、まだ関数本体の意図を明確にし、読みやすくします。
パラメータに引数のラベルをしたくない場合は、そのパラメータの明示的な引数のラベルの代わりに、アンダースコア(_) を書きます。
パラメータに引数のラベルがある場合は、関数を呼び出すときには、引数はラベル付け しなければなりません。
そのパラメータの型の後にパラメータに値を割り当てることによって、関数内の任意のパラメータに デフォルト値 を定義できます。デフォルト値が定義されている場合は、関数を呼び出すとき、そのパラメータを省略できます。
デフォルト値を持つパラメータの前に、関数のパラメータリストの先頭にデフォルト値のないパラメータを置きます。デフォルト値を持たないパラメータは、関数の意味にとっては通常、より重要であり、それらを最初に書くことは、デフォルトパラメータが省略されているかどうかに関係なく、同じ関数が呼び出されていることの認識を容易にします。
可変引数のパラメータ は、指定された型のゼロ個以上の値を受け入れます。関数が呼び出されたとき、入力された値の様々な数をパラメータに渡すことができるように指定するには、可変引数のパラメータを使用して下さい。パラメータの型名の後に3つのピリオド文字(...)を挿入することによって、可変引数のパラメータを書くことができます。
可変引数のパラメータに渡された値は適切な型の配列として、関数の本体内で利用できるようになります。例えば、Double... の型の numbers と言う名の可変引数のパラメータは、[Double] 型の numbers という定数の配列として関数の本体内で使用可能になります。
以下の例は、任意の長さの数字のリストについて、(平均 として知られている)算術平均 を計算します。
関数は、複数の可変個パラメータを持てます。可変引数パラメータの後に続く最初のパラメータには、引数ラベルがなければなりません。引数ラベルにより、どの引数が可変引数パラメータに渡され、どの引数が可変引数パラメータの後に続くパラメータに渡されるかが明確になります。
関数のパラメータは、デフォルトでは定数です。その関数の本体内から関数パラメータの値を変更しようとすると、コンパイル時エラーになります。これは、あなたが誤ってパラメータの値を変更できないことを意味します。関数でパラメータの値を変更したい場合や、関数呼び出しが終了した後にそれらの変更を持続したい場合は、代わりに in-out パラメータ としてそのパラメータを定義します。
パラメータの型の直前に inout キーワードを書くことによって、in-out パラメータを記述します。in-out パラメータが関数 内(in) に渡された値は、関数によって変更されており、元の値を置き換えるために関数の 外(out) に戻されます。in-out パラメータの動作と関連するコンパイラの最適化についての詳細は、In-Out パラメータ を参照してください。
in-out パラメータの引数としては変数だけを渡すことができます。定数およびリテラル値は変更できないので、定数またはリテラル値は引数として渡すことはできません。それが関数によって変更されうることを示すために、in-out パラメータに引数として渡すときには、変数の名前の直前にアンパサンド(&)を置いてください。
ここで、swapTwoInts(_:_:) と言う関数の例を示しますが、それには a と b と言う2つの in-out 整数パラメータがあります。
swapTwoInts(_:_:) 関数は単に b の値を a に、および a の値を b と交換します。この関数は、temporaryA と呼ばれる一時的な定数に a の値を保存し、a に b の値を代入し、次に temporaryA を b を代入することで、この交換を実行します。
それらの値を交換するのに Int 型の2つの変数を持つ swapTwoInts(_:_:) 関数を呼び出すことができます。それらは swapTwoInts(_:_:) 関数に渡されたとき someInt と anotherInt の名前はアンパサンド(&) が付いていることに注意してください:
上記の例では、それらは元々関数の外で定義されていたにもかかわらず、someInt と anotherInt の元の値が swapTwoInts(_:_:) 関数によって変更されていることを表しています。
すべての関数は、パラメータの型と関数の戻り値の型からなる特定の 関数型 を有します。
例えば:
この例では、addTwoInts と multiplyTwoInts と言う2つの単純な数学関数を定義しています。これらの関数は、それぞれ2つの Int 値をとり、適切な数学的演算を行った結果である Int 値を返します。
これらの関数の両方の型は (Int, Int) -> Int です。これは次のように読むことができます:
「2つのパラメータ、両方とも Int 型を持ち、Int 型の値を返す関数です。」
ここでは別の例を挙げ、パラメータも戻り値もない関数を考えます。
この関数の型は ( )-> Void、または「パラメータのない、Void を返す関数」です。
ちょうど Swift の他の型のように関数型を使用できます。たとえば、定数または変数は関数型に定義でき、その変数に適切な関数を割り当てることができます。
これは次のように読むことができます:
「mathFunction という変数を定義し、それは『2つの Int 値をとる関数で、Int 値を返す。』という型を持っています。この新しい変数に addTwoInts と言う関数を参照するように設定します。」
addTwoInts(_:_:) 関数は mathFunction 変数と同じ型を持つので、この代入は、Swift の型チェッカーで許可されます。
これで、mathFunction と言う名前で代入された関数を呼び出すことができます。
同じ一致する型を持つ異なる関数を、非関数型の場合と同様に、同じ変数に割り当てることができます。
他の型と同じように、定数または変数に関数を割り当てるときには関数型を推論する事を Swift に任せることができます:
(Int, Int) -> Int のような関数型を別の関数のパラメータ型として使用できます。これで、関数が呼び出されたときに提供するため関数の呼び出し元に関数の実装のいくつかの側面を任せることができます。
ここでは上述した数学関数の結果を印刷する例を示します。
この例では、3つのパラメータのある printMathResult(_:_:_:) という関数を定義しています。最初のパラメータは mathFunction と呼ばれ、(Int, Int) -> Int 型のものです。この最初のパラメータの引数としてその型の関数をどれでも渡すことができます。2番目と3番目のパラメータは、a と b と呼ばれ、両方共 Int 型です。これらは用意された数学関数のための2つの入力値として使用されます。
printMathResult(_:_:_:) が呼び出されると、それは addTwoInts(_:_:) 関数を渡され、整数値 3 と 5 を渡されます。それは、用意された関数を 3 と 5 の値で呼び出し、結果の 8 を印刷します。
printMathResult(_:_:_:) の役割は、適切な型の数学関数への呼び出しの結果を印刷することです。その関数の実装が実際にどうなっているかは問題でなく、関数が正しい型であることだけが重要です。これは、型安全の方法で、関数の呼び出し元に、その機能の一部を渡すのを printMathResult(_:_:_:) に可能にします。
別の関数の戻り値の型として関数型を使用できます。返す関数の、戻り矢印 (->) の直後に完全な関数型を書くことで、これを行えます。
次の例では、stepForward(_:) と stepBackward(_:) と言う簡単な関数を2つ定義しています。stepForward(_:) 関数はその入力値より一つだけ大きいものを返し、stepBackward(_:) 関数は、その入力値より一つだけ小さい値を返します。どちらの関数も (Int) -> Int の型です。
ここで、chooseStepFunction(backward:) と言う関数を考え、それは戻り値の型が (Int) -> Int 型の関数です。 chooseStepFunction(backward:) 関数は、backward と言うブール値のパラメータに基づいて stepForward(_:) 関数あるいは stepBackward(_:) 関数を返します。
これで、一方向または他の方向に進む関数を取得するために chooseStepFunction(backward:) を使用できます。
上述の例では、currentValue と言う変数をゼロに近づけるために正か負のステップのどちらが必要であるかを判定します。currentValue は 3 の初期値を持っており、それは currentValue > 0 が true を返す事を意味し、 chooseStepFunction(backward:) は stepBackward(_:) 関数を返します。返される関数への参照は、 moveNearerToZero と言う定数に保存されます。
今や moveNearerToZero は正しい関数を参照しており、カウントをゼロにするために使用できます。
この章でこれまでお見せした関数はすべて、グローバルな範囲で定義されている グローバルな関数 の例でした。他の関数の本体内に関数を定義することもでき、入れ子になった関数 として知られています。
入れ子になった関数は、デフォルトでは外の世界から隠されていますが、それらを取り囲む関数で呼び出され、使用することができます。取り囲んでいる関数は、またその入れ子になった関数の一つを返すこともでき、入れ子になった関数が別の範囲内で使用できるようにできます。
上記の chooseStepFunction(backward:) の例を書き換えて、入れ子になった関数を使用して返すことができます。