🐦
Swift言語のサンプルコード
文法解説で学んだ内容を組み合わせた、難易度が段階的に上がる3本の実践的なサンプルプログラムを紹介します。
サンプル1:struct と protocol で図形の面積を計算する
protocolで「面積を持つ図形」という共通のふるまいを定義し、CircleとRectangleという異なるstructがそれぞれ準拠します。呼び出す側は具体的な型を意識せず、Shapeプロトコル越しに統一的に扱えます(ポリモーフィズム)。structは値型なので、配列にコピーされて格納されても意図しない副作用が起きません。
SAMPLE PROGRAM
protocol Shape {
var name: String { get }
var area: Double { get }
}
struct Circle: Shape {
let radius: Double
var name: String { "円" }
var area: Double { Double.pi * radius * radius }
}
struct Rectangle: Shape {
let width: Double
let height: Double
var name: String { "長方形" }
var area: Double { width * height }
}
func printReport(_ shapes: [Shape]) {
var total = 0.0
for shape in shapes {
let areaText = String(format: "%.2f", shape.area)
print("\(shape.name): 面積 \(areaText)")
total += shape.area
}
let totalText = String(format: "%.2f", total)
print("合計面積: \(totalText)")
}
let shapes: [Shape] = [
Circle(radius: 3),
Rectangle(width: 4, height: 5)
]
printReport(shapes)
// 円: 面積 28.27
// 長方形: 面積 20.00
// 合計面積: 48.27
サンプル2:スタックを使った括弧チェックと二分探索
1つ目はスタック(後入れ先出し)を使い、文字列内の括弧の対応が正しいかを判定するプログラムです。開き括弧が来たらスタックに積み、閉じ括弧が来たら直前に積んだ開き括弧と対応するか確認します。2つ目はソート済み配列に対する二分探索で、計算量はO(log n)です(文法解説の14番を参照)。
SAMPLE PROGRAM
// 1) 括弧の対応をスタックでチェックする
func isValidParentheses(_ text: String) -> Bool {
var stack: [Character] = []
let pairs: [Character: Character] = [")": "(", "]": "[", "}": "{"]
for char in text {
if char == "(" || char == "[" || char == "{" {
stack.append(char) // 開き括弧を積む
} else if let open = pairs[char] {
if stack.popLast() != open { // 対応しない、または空なら不正
return false
}
}
}
return stack.isEmpty // 最後に閉じ残しがなければ正しい
}
print(isValidParentheses("{[()()]}")) // true
print(isValidParentheses("{[(])}")) // false
// 2) 二分探索:O(log n)
func binarySearch(_ array: [Int], target: Int) -> Int? {
var low = 0
var high = array.count - 1
while low <= high {
let mid = (low + high) / 2
if array[mid] == target {
return mid
} else if array[mid] < target {
low = mid + 1
} else {
high = mid - 1
}
}
return nil
}
let sortedNumbers = [1, 3, 5, 7, 9, 11, 13]
if let index = binarySearch(sortedNumbers, target: 9) {
print("9はインデックス\(index)で見つかりました")
}
サンプル3:Optional型とエラーハンドリングによる入力バリデーション
フォーム入力のように「値が空かもしれない/形式が不正かもしれない」状況を、Optional型・guard let・独自のErrorenum・do/try/catch・Result型を組み合わせて安全に処理する例です。エラーの種類ごとにassociated valueで詳細情報を持たせ、呼び出し側で個別にハンドリングしています。
SAMPLE PROGRAM
enum ValidationError: Error {
case empty
case invalidFormat
case tooYoung(age: Int)
}
struct UserInput {
let nameText: String?
let ageText: String?
}
func validate(_ input: UserInput) throws -> (name: String, age: Int) {
guard let nameText = input.nameText, !nameText.isEmpty else {
throw ValidationError.empty
}
guard let ageText = input.ageText, let age = Int(ageText) else {
throw ValidationError.invalidFormat
}
guard age >= 18 else {
throw ValidationError.tooYoung(age: age)
}
return (nameText, age)
}
func register(_ input: UserInput) {
do {
let result = try validate(input)
print("登録OK: \(result.name)さん(\(result.age)歳)")
} catch ValidationError.empty {
print("名前が空です")
} catch ValidationError.invalidFormat {
print("年齢の形式が不正です")
} catch ValidationError.tooYoung(let age) {
print("18歳未満のため登録できません(\(age)歳)")
} catch {
print("不明なエラー: \(error)")
}
}
register(UserInput(nameText: "花子", ageText: "17"))
register(UserInput(nameText: "太郎", ageText: "20"))
// Result型で成功/失敗を表現する例
func divide(_ a: Int, _ b: Int) -> Result<Int, ValidationError> {
guard b != 0 else { return .failure(.invalidFormat) }
return .success(a / b)
}
switch divide(10, 0) {
case .success(let value):
print("計算結果: \(value)")
case .failure(let error):
print("エラー: \(error)")
}