詳解Swift改訂版(02)〜関数
2016. 12. 28
この記事は詳解 Swift 改訂版(初版第1刷)を元に、Xcode 8.2.1(Swift 3.0.2)下における差異を記述しています。
CHAPTER 02 関数
●2.1 関数定義の基本
・関数定義の概要
List2-1 において、関数の引数のラベルは第1引数でも必要になりました。
「Qiita/Xcode 8 Release Notes 日本語翻訳メモ」によると、Swift 3 で関数の引数は全ての引数でラベル付けされることになったようです。
これに関しては「Xcode 8 Release Notes(Xcode 8.0/Swift/New Features)」に、その旨の記述があります。
「関数の引数は現在、全ての関数の引数に渡って一貫したラベル付けを行うようになりました。
このアップデートでは第1引数の宣言が、これまでの第2以降の引数の動作と一致するようになりました。
以前に書かれたコードでは、
となっていましたが、現在は次のように記述します。
(SE-0046)」
したがって関数 count の呼び出しは以下のようになります。
・外部引数名
上記の通り関数は全ての引数でラベル付けされるようになりましたので、これまでの Objective-C のメソッド記法のように関数名に第1引数の意味を含める必要は無くなったと思われます。
今後はシンプルな関数名で構わないでしょう。
List2-2 を例にすると、
関数名に第1引数の意味を含み、且つ第1引数のラベルもあって冗長になりますので、関数名から第1引数の意味を取り除いた方がいいでしょう。
また今回の例で言えば、関数名 buy だけでは単純すぎて心許なく、引数の product はカタログ番号であることから、関数名をそのままに第1引数名を number とする手もあるでしょう。
もしくは「_」で第1引数の外部引数名を省略することで、これまでと同じ挙動にすることもできます。
いずれにせよ、これまでの記法には再考の余地があります。
※ 以降において、第1引数のラベル付けに関する修整については、分かり易さのために「_」で呼び出し時の外部引数名を省略することにし、特別な理由がなければ説明を割愛します。
・外部引数名の指定と省略
上記の通り第1引数も自動的に外部引数名が付与されることになりましたが、
これまでと同様、内部引数名と別途に外部引数名を指定することもできます。
●2.2 関数定義におけるさまざまな設定
・inout 引数
List 2-3 において、inout 引数でエラーが出るようになりました。
「Qiita/Xcode 8 Release Notes 日本語翻訳メモ」によると、Swift 3 で inout 引数は : の後ろに書くことになったようです。
これに関しては「Xcode 8 Release Notes(Xcode 8.0/Swift/New Features)」に、その旨の記述があります。
「inout 属性の場所が : の後ろ且つ引数型の前に移動されています。
例えば以前に書かれたコードでは、
ですが、現在は次のように記述する必要があります。
(SE-0031)」
したがって関数 mySwap の引数は以下のようになります。
・関数の引数に規定値を指定する
List2-4 の関数 setFont の実行結果の例で、規定値を指定した引数の順序を入れ替えた場合にエラーが出るようになりました。
「Qiita/Xcode 8 Release Notes 日本語翻訳メモ」によると、Swift 3 で規定値を持つ引数は宣言順に指定することになったようです。
これに関しては「Xcode 8 Release Notes(Xcode 8.0/Swift/New Features)」に、その旨の記述があります。
「デフォルト値を持つ関数の引数は、現在宣言順に指定する必要があります。
呼び出し時に提供する引数の位置は、常に宣言された順序で関数に渡す必要があります。
指定された引数が宣言の順序に従う限り、デフォルト値を持つ任意のラベル付き引数を省略することができます。
(SE-0060)」
したがって関数 setFont の引数は以下のようになります。
・引数の値を処理中に変更できるようにする
List2-6 の関数 dayOfWeek において、引数への var 修飾語の付与はエラーが出るようになりました。
「Qiita/Xcode 8 Release Notes 日本語翻訳メモ」によると、Swift 3 で関数の引数で var が使えなくなったようです。
これに関しては「Xcode 8 Release Notes(Xcode 8.0/Swift/New Features)」に、その旨の記述があります。
「var はもはや関数の引数属性として認められません。
コンパイラは関数本体にシャドウコピーを作成するための修正を提供します。
例えば以前に書かれたコードが、
となっていた場合、現在は次のように記述する必要があります。
(SE-0003)」
したがって関数 dayOfWeek の引数指定は以下のようになります。
また本文中で「変更しないことを明示するために let という修飾語を使うことができます。」とありますが、「Qiita/Xcode 8 Release Notes 日本語翻訳メモ」にSwift 3で無駄な let は削除されたとあり、「Xcode 8 Release Notes(Xcode 8.0/Swift/New Features)」に、その旨の記述があります。
「let はもはや関数の引数属性として認められません。
コンパイラは関数の宣言から削除するための修正を提供します。(SE-0053)」
例えば関数 dayOfWeek の引数 d に let を指定すると警告が出ます。
・返り値を必ず使うための指定
返り値を伴う関数に @warn_unused_result 属性を指定すると警告が出るようになりました。
「Tomorrow Never Comes./[Swift]関数の返り値を使わない時は」によると、Swift 3 から関数の返り値を使用しない場合はデフォルトで警告が出るようになったため、@warn_unused_result は不要になったようです。
逆に返り値を使用しないことを明示するには @discardableResult 属性を指定することになります。
ただしプレイグラウンドでは、返り値を伴う関数を返り値を使用しない形で実行しても警告が出ないようなので注意が必要です。
・Qiita/Xcode 8 Release Notes 日本語翻訳メモ
・Tomorrow Never Comes./[Swift]関数の返り値を使わない時は
・詳解Swift 第3版(Amazon)
CHAPTER 02 関数
●2.1 関数定義の基本
・関数定義の概要
List2-1 において、関数の引数のラベルは第1引数でも必要になりました。
func count(n: Int) -> Int {
total += n
return total
}
count(10) // error: missing argument label 'n:' in call
total += n
return total
}
count(10) // error: missing argument label 'n:' in call
「Qiita/Xcode 8 Release Notes 日本語翻訳メモ」によると、Swift 3 で関数の引数は全ての引数でラベル付けされることになったようです。
これに関しては「Xcode 8 Release Notes(Xcode 8.0/Swift/New Features)」に、その旨の記述があります。
「関数の引数は現在、全ての関数の引数に渡って一貫したラベル付けを行うようになりました。
このアップデートでは第1引数の宣言が、これまでの第2以降の引数の動作と一致するようになりました。
以前に書かれたコードでは、
func Resolved Issues(x: Int, y: Int) {}
Resolved Issues(1, y: 2)
func bar(a a: Int, b: Int) {}
bar(a: 3, b: 4)
Resolved Issues(1, y: 2)
func bar(a a: Int, b: Int) {}
bar(a: 3, b: 4)
となっていましたが、現在は次のように記述します。
func Resolved Issues(_ x: Int, y: Int) {}
Resolved Issues(1, y: 2)
func bar(a: Int, b: Int) {}
bar(a: 3, b: 4)
Resolved Issues(1, y: 2)
func bar(a: Int, b: Int) {}
bar(a: 3, b: 4)
(SE-0046)」
したがって関数 count の呼び出しは以下のようになります。
count(n: 10)
・外部引数名
上記の通り関数は全ての引数でラベル付けされるようになりましたので、これまでの Objective-C のメソッド記法のように関数名に第1引数の意味を含める必要は無くなったと思われます。
今後はシンプルな関数名で構わないでしょう。
List2-2 を例にすると、
func buyProduct(product:Int, price:Int, quantity:Int) {
print("Product:\(product), amount = \(price * quantity)")
}
buyProduct(product: 19090, price: 180000, quantity: 1) // Product:19090, amount = 180000
print("Product:\(product), amount = \(price * quantity)")
}
buyProduct(product: 19090, price: 180000, quantity: 1) // Product:19090, amount = 180000
関数名に第1引数の意味を含み、且つ第1引数のラベルもあって冗長になりますので、関数名から第1引数の意味を取り除いた方がいいでしょう。
func buy(product:Int, price:Int, quantity:Int) {
print("Product:\(product), amount = \(price * quantity)")
}
buy(product: 19090, price: 180000, quantity: 1) // Product:19090, amount = 180000
print("Product:\(product), amount = \(price * quantity)")
}
buy(product: 19090, price: 180000, quantity: 1) // Product:19090, amount = 180000
また今回の例で言えば、関数名 buy だけでは単純すぎて心許なく、引数の product はカタログ番号であることから、関数名をそのままに第1引数名を number とする手もあるでしょう。
func buyProduct(number:Int, price:Int, quantity:Int) {
print("Number:\(number), amount = \(price * quantity)")
}
buyProduct(number: 19090, price: 180000, quantity: 1) // Number:19090, amount = 180000
print("Number:\(number), amount = \(price * quantity)")
}
buyProduct(number: 19090, price: 180000, quantity: 1) // Number:19090, amount = 180000
もしくは「_」で第1引数の外部引数名を省略することで、これまでと同じ挙動にすることもできます。
func buyProduct(_ product:Int, price:Int, quantity:Int) {
print("Product:\(product), amount = \(price * quantity)")
}
buyProduct(19090, price: 180000, quantity: 1) // Product:19090, amount = 180000
print("Product:\(product), amount = \(price * quantity)")
}
buyProduct(19090, price: 180000, quantity: 1) // Product:19090, amount = 180000
いずれにせよ、これまでの記法には再考の余地があります。
※ 以降において、第1引数のラベル付けに関する修整については、分かり易さのために「_」で呼び出し時の外部引数名を省略することにし、特別な理由がなければ説明を割愛します。
・外部引数名の指定と省略
上記の通り第1引数も自動的に外部引数名が付与されることになりましたが、
func area(h:Double, w:Double) -> Double {
return h * w
}
let a = area(h: 10.0, w: 12.5)
return h * w
}
let a = area(h: 10.0, w: 12.5)
これまでと同様、内部引数名と別途に外部引数名を指定することもできます。
func area(height h:Double, width w:Double) -> Double {
return h * w
}
let a = area(height: 10.0, width: 12.5)
return h * w
}
let a = area(height: 10.0, width: 12.5)
●2.2 関数定義におけるさまざまな設定
・inout 引数
List 2-3 において、inout 引数でエラーが出るようになりました。
func mySwap(inout _ a:Int, inout _ b:Int) { // 'inout' before a parameter name is not allowed. place it before the parameter type instead
「Qiita/Xcode 8 Release Notes 日本語翻訳メモ」によると、Swift 3 で inout 引数は : の後ろに書くことになったようです。
これに関しては「Xcode 8 Release Notes(Xcode 8.0/Swift/New Features)」に、その旨の記述があります。
「inout 属性の場所が : の後ろ且つ引数型の前に移動されています。
例えば以前に書かれたコードでは、
func Resolved Issues(inout x: Int) {
}
}
ですが、現在は次のように記述する必要があります。
func Resolved Issues(x: inout Int) {
}
}
(SE-0031)」
したがって関数 mySwap の引数は以下のようになります。
func mySwap(_ a: inout Int, _ b: inout Int) {
・関数の引数に規定値を指定する
List2-4 の関数 setFont の実行結果の例で、規定値を指定した引数の順序を入れ替えた場合にエラーが出るようになりました。
setFont(name: "Times", bold:true, size:18.0) // error: argument 'size' must precede argument 'bold'
「Qiita/Xcode 8 Release Notes 日本語翻訳メモ」によると、Swift 3 で規定値を持つ引数は宣言順に指定することになったようです。
これに関しては「Xcode 8 Release Notes(Xcode 8.0/Swift/New Features)」に、その旨の記述があります。
「デフォルト値を持つ関数の引数は、現在宣言順に指定する必要があります。
呼び出し時に提供する引数の位置は、常に宣言された順序で関数に渡す必要があります。
func requiredArguments(a: Int, b: Int, c: Int) {}
func defaultArguments(a: Int = 0, b: Int = 0, c: Int = 0) {}
requiredArguments(a: 0, b: 1, c: 2)
requiredArguments(b: 0, a: 1, c: 2) // error
defaultArguments(a: 0, b: 1, c: 2)
defaultArguments(b: 0, a: 1, c: 2) // error
func defaultArguments(a: Int = 0, b: Int = 0, c: Int = 0) {}
requiredArguments(a: 0, b: 1, c: 2)
requiredArguments(b: 0, a: 1, c: 2) // error
defaultArguments(a: 0, b: 1, c: 2)
defaultArguments(b: 0, a: 1, c: 2) // error
指定された引数が宣言の順序に従う限り、デフォルト値を持つ任意のラベル付き引数を省略することができます。
defaultArguments(a: 0) // ok
defaultArguments(b: 1) // ok
defaultArguments(c: 2) // ok
defaultArguments(a: 1, c: 2) // ok
defaultArguments(b: 1, c: 2) // ok
defaultArguments(c: 1, b: 2) // error
defaultArguments(b: 1) // ok
defaultArguments(c: 2) // ok
defaultArguments(a: 1, c: 2) // ok
defaultArguments(b: 1, c: 2) // ok
defaultArguments(c: 1, b: 2) // error
(SE-0060)」
したがって関数 setFont の引数は以下のようになります。
setFont(name: "Times", size:18.0, bold:true) // Times 18.0 [B]
・引数の値を処理中に変更できるようにする
List2-6 の関数 dayOfWeek において、引数への var 修飾語の付与はエラーが出るようになりました。
func dayOfWeek(var _ m:Int, _ d:Int, var year y:Int = 2016) -> Int { // error: parameters may not have the 'var' specifier
「Qiita/Xcode 8 Release Notes 日本語翻訳メモ」によると、Swift 3 で関数の引数で var が使えなくなったようです。
これに関しては「Xcode 8 Release Notes(Xcode 8.0/Swift/New Features)」に、その旨の記述があります。
「var はもはや関数の引数属性として認められません。
コンパイラは関数本体にシャドウコピーを作成するための修正を提供します。
例えば以前に書かれたコードが、
func Resolved Issues(var x: Int) {
}
}
となっていた場合、現在は次のように記述する必要があります。
func Resolved Issues(x: Int) {
var x = x
}
var x = x
}
(SE-0003)」
したがって関数 dayOfWeek の引数指定は以下のようになります。
func dayOfWeek(_ m:Int, _ d:Int, year y:Int = 2016) -> Int {
var m = m, y = y
var m = m, y = y
また本文中で「変更しないことを明示するために let という修飾語を使うことができます。」とありますが、「Qiita/Xcode 8 Release Notes 日本語翻訳メモ」にSwift 3で無駄な let は削除されたとあり、「Xcode 8 Release Notes(Xcode 8.0/Swift/New Features)」に、その旨の記述があります。
「let はもはや関数の引数属性として認められません。
コンパイラは関数の宣言から削除するための修正を提供します。(SE-0053)」
例えば関数 dayOfWeek の引数 d に let を指定すると警告が出ます。
func dayOfWeek(_ m:Int, let _ d:Int, year y:Int = 2016) -> Int { // 'let' as a parameter attribute is not allowed
・返り値を必ず使うための指定
返り値を伴う関数に @warn_unused_result 属性を指定すると警告が出るようになりました。
@warn_unused_result // 'warn_unused_result' attribute behavior is now the default
func dayOfWeek(_ m:Int, _ d:Int, year y:Int = 2016) -> Int {
func dayOfWeek(_ m:Int, _ d:Int, year y:Int = 2016) -> Int {
「Tomorrow Never Comes./[Swift]関数の返り値を使わない時は」によると、Swift 3 から関数の返り値を使用しない場合はデフォルトで警告が出るようになったため、@warn_unused_result は不要になったようです。
逆に返り値を使用しないことを明示するには @discardableResult 属性を指定することになります。
ただしプレイグラウンドでは、返り値を伴う関数を返り値を使用しない形で実行しても警告が出ないようなので注意が必要です。
・Qiita/Xcode 8 Release Notes 日本語翻訳メモ
・Tomorrow Never Comes./[Swift]関数の返り値を使わない時は
・詳解Swift 第3版(Amazon)