Swift: Swiftの関数定義とパラメータ:初心者から上級者へ
関数はSwiftにおけるコードの基本的な構成要素です。このレッスンでは、関数の定義方法とすべてのパラメータ形式を柔軟に使いこなす方法を学び、コードをより意味的で再利用可能にします。
1. 学習目標
funcキーワードを使った関数の定義と呼び出し- 引数ラベルとパラメータ名の違いの理解
- パラメータのデフォルト値の設定
inoutを使ったパラメータのインプレース変更- 可変長パラメータを使った可変個の引数の処理
2. バックエンドエンジニアの実話
(1) 課題:呼び出し元でパラメータの意味が不明
BobはEC注文システムを開発しています。彼は注文合計価格を計算する関数を書きました:
SWIFT
func calculate(price: Double, count: Int, rate: Double) -> Double {
return price * Double(count) * (1 - rate)
}
// At the call site — parameter meaning is guesswork
let result = calculate(price: 100, count: 3, rate: 0.1)
チームはrate: 0.1の意味をめぐって意見が分かれました——10%の税金と考える人もいれば、10%の割引と考える人もいました。関数シグネチャが各パラメータの意味的役割を表現していませんでした。
(2) 引数ラベルの解決策
Swiftでは各パラメータに外部ラベルと内部名の両方を定義でき、呼び出し元を自然言語のように読めるようにします:
SWIFT
func calculate(price: Double, count: Int, discount rate: Double) -> Double {
return price * Double(count) * (1 - rate)
}
// Call site is clear: "discount" tells you it's a discount rate
let result = calculate(price: 100, count: 3, discount: 0.1)
(3) 結果:意味的に明確な関数呼び出し
| 観点 | Before | After |
|---|---|---|
| パラメータの意味 | 純粋な推測 | 呼び出し元で理解可能 |
| コードの可読性 | 定義にジャンプ必須 | 1行で意味が明確 |
| チームコミュニケーション | 意味をめぐる頻繁な議論 | 自己文書化された関数シグネチャ |
| 保守効率 | パラメータ変更時に全体検索 | 局所的な変更、曖昧さなし |
3. 関数の基本定義
関数はSwiftにおける再利用可能なコードブロックで、funcキーワードで定義します。
graph LR
A[funcキーワード] --> B[関数名]
B --> C[パラメータリスト]
C --> D[戻り矢印 & 型]
D --> E[関数本体]
A --> F["func greet(name: String) -> String { return \"こんにちは \\(name)\" }"]
| 構成要素 | 必須 | 備考 |
|---|---|---|
funcキーワード |
はい | 関数宣言を示す |
| 関数名 | はい | 関数を識別、camelCaseを使用 |
パラメータリスト () |
はい | パラメータがなくても空の括弧 |
戻り値 -> 型 |
いいえ | 省略するとVoidを返す |
本体 {} |
はい | 関数が実行するコードブロック |
(1) パラメータなし・戻り値なし
SWIFT
func sayHello() {
print("Hello!")
}
sayHello() // Call
出力:
TEXT 📖 参照専用Hello!
(2) パラメータと戻り値
SWIFT
func add(a: Int, b: Int) -> Int {
return a + b
}
let sum = add(a: 5, b: 3)
print("5 + 3 = \(sum)")
出力:
TEXT 📖 参照専用5 + 3 = 8
▶ サンプル: 注文金額の計算
SWIFT
// ============================================
// Calculate order total including tax
// ============================================
func orderAmount(price: Double, quantity: Int) -> Double {
return price * Double(quantity)
}
let amount = orderAmount(price: 29.99, quantity: 3)
print("Order amount: $\(amount)")
出力:
TEXT 📖 参照専用Order amount: $89.97
4. 引数ラベルとデフォルト値
(1) 引数ラベル
Swiftでは各パラメータに外部ラベル(呼び出し元で使用)と内部名(関数本体内で使用)を指定できます:
| スタイル | 構文 | 呼び出し元 | 使用場面 |
|---|---|---|---|
| ラベル = 名前 | func foo(name: String) |
foo(name: ...) |
パラメータ名が十分に意味的 |
| 外部ラベル | func foo(with name: String) |
foo(with: ...) |
自然言語のように読める呼び出し |
| ラベル省略 | func foo(_ name: String) |
foo(...) |
動詞が既にパラメータを表現 |
| 組み合わせ | 様々なラベル | 柔軟 | カスタム要件 |
▶ サンプル: ラベルスタイルの比較
SWIFT
// ============================================
// Three argument label styles compared
// ============================================
// 1. Default — label equals name
func greet1(name: String) {
print("Hello, \(name)")
}
// 2. Custom external label
func greet2(to name: String) {
print("Hello, \(name)")
}
// 3. Omit external label (underscore)
func greet3(_ name: String) {
print("Hello, \(name)")
}
greet1(name: "Alice")
greet2(to: "Bob")
greet3("Charlie")
出力:
TEXT 📖 参照専用Hello, Alice Hello, Bob Hello, Charlie
(2) パラメータのデフォルト値
パラメータにデフォルト値を提供すると、呼び出し側はその引数を省��できます:
SWIFT
func makeCoffee(size: String, sugar: Int = 1) -> String {
return "\(size) coffee with \(sugar) sugar(s)"
}
print(makeCoffee(size: "Large")) // Uses default sugar
print(makeCoffee(size: "Medium", sugar: 0)) // Custom sugar
出力:
TEXT 📖 参照専用Large coffee with 1 sugar(s) Medium coffee with 0 sugar(s)💡 ヒント: デフォルトパラメータはパラメータリストの末尾に配置して、呼び出し元での曖昧さを避けます。
5. 可変長パラメータとinout
(1) 可変長パラメータ
可変長パラメータは指定された型の0個以上の値を受け取り、関数本体内では配列として利用できます:
SWIFT
func sum(_ numbers: Double...) -> Double {
var total = 0.0
for number in numbers {
total += number
}
return total
}
print(sum(1, 2, 3)) // 3 arguments
print(sum(10, 20)) // 2 arguments
print(sum()) // 0 arguments
出力:
TEXT 📖 参照専用6.0 30.0 0.0
| 特性 | 説明 |
|---|---|
| 構文 | 型の後に...を追加 |
| 個数 | 0から任意の数 |
| 型 | すべての値が同じ型でなければならない |
| 位置 | 関数ごとに最大1つ。最後のパラメータにするべき |
(2) inoutパラメータ
inoutパラメータを使うと、関数が呼び出し元の変数を変更できます——本質的には参照渡しで、コピーではありません:
SWIFT
// ============================================
// inout parameter: swapping two variables
// ============================================
func swapValues(_ a: inout Int, _ b: inout Int) {
let temp = a
a = b
b = temp
}
var x = 10
var y = 20
swapValues(&x, &y)
print("x = \(x), y = \(y)")
出力:
TEXT 📖 参照専用x = 20, y = 10⚠️ 注意:
inoutパラメータに渡す際に変数の前に&を付けます。定数やリテラルは渡せません。
▶ サンプル: 汎用平均計算機
SWIFT
// ============================================
// Variadic + inout combined: compute average and track calls
// ============================================
func average(_ scores: Double..., record callCount: inout Int) -> Double {
callCount += 1
guard !scores.isEmpty else { return 0 }
var total = 0.0
for score in scores {
total += score
}
return total / Double(scores.count)
}
var callTimes = 0
print("Average: \(average(85, 90, 78, record: &callTimes))")
print("Average: \(average(100, 95, record: &callTimes))")
print("Function called \(callTimes) times")
出力:
TEXT 📖 参照専用Average: 84.33333333333333 Average: 97.5 Function called 2 times
6. 完全な例:注文割引計算機
SWIFT
// ============================================
// Full example: Order discount calculator
// Combines argument labels, defaults, variadic, inout
// ============================================
import Foundation
// 1. Compute subtotal (variadic)
func subtotal(_ prices: Double...) -> Double {
var total = 0.0
for price in prices {
total += price
}
return total
}
// 2. Apply discount (default parameter + argument label)
func applyDiscount(to total: Double, rate: Double = 0.1) -> Double {
return total * (1 - rate)
}
// 3. Log orders (inout parameter)
func logOrder(_ description: String, counter: inout Int) {
counter += 1
print("[\(counter)] \(description)")
}
// 4. Simulate order processing
var orderCount = 0
let item1 = subtotal(29.99, 49.99)
let item2 = subtotal(9.99, 14.99, 24.99)
logOrder("Item 1 subtotal: $\(item1)", counter: &orderCount)
logOrder("Item 2 subtotal: $\(item2)", counter: &orderCount)
let total1 = applyDiscount(to: item1, rate: 0.15)
let total2 = applyDiscount(to: item2)
let finalTotal = total1 + total2
logOrder("Final total: $\(finalTotal)", counter: &orderCount)
出力:
TEXT 📖 参照専用[1] Item 1 subtotal: $79.98 [2] Item 2 subtotal: $49.97 [3] Final total: $112.94
❓ よくある質問
Q 引数ラベルとパラメータ名の違いは?
A 引数ラベル(外部名)は呼び出し元で使用されます。パラメータ名(内部名/ローカル名)は関数本体内で使用されます。前者は呼び出し元の可読性に、後者は内部の意味付けに役立ちます。同じにも異なるにもできます。
Q inoutとグローバル変数の違いは?
A inoutパラメータは関数内でローカル変数のように振る舞い、関数が戻るときにのみ元の変数に書き戻します。グローバル変数は直接読み書きできますが、保守性とテスト容易性が低下します。inoutの方が安全な選択です。
Q 可変長パラメータと配列パラメータの違いは?
A 可変長パラメータは呼び出し元で明示的に配列を構築する必要がなく、カンマ区切りで値を渡すだけです。配列パラメータは
[1, 2, 3]の形式が必要です。可変長の方が簡潔です。Q 関数に複数の可変長パラメータを持てますか?
A いいえ。各関数は最大1つの可変長パラメータしか持てず、通常はパラメータリストの最後に置くべきです。複数の可変長パラメータが必要な場合は、代わりに配列パラメータの使用を検討してください。
Q 矢印を省略した場合、関数は何を返しますか?
A
-> 型を省略するとVoid(空のタプル())を返すのと同じです。関数本体ではreturn文を省略できます。例えば、func log() { print("log") }はVoidを返します。📖 まとめ
- 関数は
funcキーワードで定義。パラメータリストと本体が基本構造 - 引数ラベルにより関数呼び出しがより自然言語らしく読める——各パラメータに適切な外部ラベルを設計
- デフォルトパラメータ値は繰り返しの呼び出しコードを減らし、パラメータリストの末尾に置くべき
- 可変長パラメータ
...は同じ型の0個以上の値を受け取り、本体内では配列として扱われる - inoutパラメータにより参照渡しが可能になり、呼び出し元の変数を変更できる。呼び出し元では
&を前置
📝 練習問題
- 初級:
name(String)とgreeting(String、デフォルト「こんにちは」)を受け取り、挨拶を出力するgreet関数を作成してください。少なくとも2つの異なる引数ラベルスタイルで呼び出してください。 - 中級:
weight(kg、Double)とheight(m、Double)を受け取り、BMI値を返すcalculateBMI関数を作成してください。呼び出し元がcalculateBMI(weight: 75, height: 1.8)のように読める引数ラベルを使用してください。また、呼び出し回数を追跡するinoutパラメータも追加してください。 - 上級: 可変長パラメータを使って任意の数の点数を受け取り、最高点、最低点、平均点を返す
gradesAnalyzer関数を作成してください。分析履歴を保持するinoutパラメータ(各分析の回数を配列に追加)を使用してください。最後に履歴を出力します。