Swift: Swiftの関数定義とパラメータ:初心者から上級者へ

関数はSwiftにおけるコードの基本的な構成要素です。このレッスンでは、関数の定義方法とすべてのパラメータ形式を柔軟に使いこなす方法を学び、コードをより意味的で再利用可能にします。

1. 学習目標


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キーワードで定義します。

100%
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を返します。

📖 まとめ


📝 練習問題

  1. 初級: name(String)とgreeting(String、デフォルト「こんにちは」)を受け取り、挨拶を出力するgreet関数を作成してください。少なくとも2つの異なる引数ラベルスタイルで呼び出してください。
  2. 中級: weight(kg、Double)とheight(m、Double)を受け取り、BMI値を返すcalculateBMI関数を作成してください。呼び出し元がcalculateBMI(weight: 75, height: 1.8)のように読める引数ラベルを使用してください。また、呼び出し回数を追跡するinoutパラメータも追加してください。
  3. 上級: 可変長パラメータを使って任意の数の点数を受け取り、最高点、最低点、平均点を返すgradesAnalyzer関数を作成してください。分析履歴を保持するinoutパラメータ(各分析の回数を配列に追加)を使用してください。最後に履歴を出力します。
Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%