イテレータパターン
CoddyのCジャーニー「Object Oriented Programming」セクションの一部 — レッスン 56/61。
イテレータパターンは、コレクションの内部構造を露出させることなく、その要素に順次アクセスする方法を提供します。ユーザーに配列への直接的なアクセスを許可する代わりに、データを一度に1要素ずつ処理する方法を知っているオブジェクトを提供します。
イテレータは通常、コレクションへの参照と現在の位置という2つの情報を必要とします。C言語では、この状態を保持するために構造体を作成します。
typedef struct {
int* data; // 配列へのポインタ
int size; // 要素の総数
int current; // 現在の位置
} IntIterator;イテレータは2つの主要な関数を公開しています。has_next() は読み取る要素がまだあるかどうかを確認し、next() は現在の要素を返し、位置を進めます:
int has_next(IntIterator* it) {
return it->current < it->size;
}
int next(IntIterator* it) {
return it->data[it->current++];
}イテレータを使用すると、見た目がすっきりし、配列の詳細が隠されます:
IntIterator it = create_iterator(numbers, 5);
while (has_next(&it)) {
printf("%d\n", next(&it));
}呼び出し側は、numbers が配列であることや、インデックス作成がどのように機能するかを知る必要は全くありません。この抽象化により、イテレータを使用するコードを変更することなく、後で基礎となるデータ構造を(例えば配列から連結リストへ)簡単に変更できるようになります。
チャレンジ
簡単NumberList イテレータを構築しましょう。これは、基盤となる配列構造を公開せずに整数のコレクションをトラバースできるようにする、クリーンな抽象化です。
コードは以下の3つのファイルに分けて構成します:
iterator.h: 整数配列へのポインタ、コレクションの総サイズ、および現在の位置を保持するIntIterator構造体を定義します。3つの関数を宣言します:create_iterator(配列ポインタとサイズを受け取り、初期化されたイテレータを返す)、has_next(さらに要素が残っているかを確認する)、およびnext(現在の要素を返し、位置を進める)。インクルードガードを忘れないでください!iterator.c: イテレータ関数を実装します。create_iterator関数は、現在の位置を 0 に設定したIntIteratorを値で返す必要があります。has_next関数は、読み取る要素がまだある場合は 1 を、そうでない場合は 0 を返します。next関数は、現在の位置にある要素を返し、その後に位置をインクリメントします。main.c: 要素の数を読み取り、次に各整数値を配列に読み込みます。この配列のイテレータを作成し、has_nextとnextを使用した while ループで各要素をトラバースして、それぞれの要素を個別の行に出力します。
プログラムは以下を受け取ります:
- 配列内の要素数
- 各整数値(個別の行)
イテレータパターンを使用してすべての要素を出力してください。トラバースループ内での直接的な配列インデックス参照は禁止です!
入力が 4、次に 10, 20, 30, 40 の場合の出力例:
10
20
30
40入力が 3、次に -5, 0, 100 の場合の出力例:
-5
0
100入力が 1、次に 42 の場合の出力例:
42イテレータはデータがどのように格納されているかを隠蔽します。メインループは、配列のインデックスやメモリレイアウトについて何も知ることなく、単に「次はありますか?」と「次のものをください」と要求するだけです。
チートシート
イテレータパターンは、コレクションの内部構造を露出させることなく、その要素に順番にアクセスする方法を提供します。
イテレータの構造体は、コレクションへの参照、サイズ、および現在の位置を保持します。
typedef struct {
int* data; // 配列へのポインタ
int size; // 要素の総数
int current; // 現在の位置
} IntIterator;イテレータは2つの主要な関数を公開します。
int has_next(IntIterator* it) {
return it->current < it->size;
}
int next(IntIterator* it) {
return it->data[it->current++];
}イテレータを使用することで、配列の実装の詳細が隠蔽されます。
IntIterator it = create_iterator(numbers, 5);
while (has_next(&it)) {
printf("%d\n", next(&it));
}この抽象化により、クライアントコードを修正することなく、基盤となるデータ構造を変更することが可能になります。
自分で試してみよう
#include <stdio.h>
#include "iterator.h"
int main() {
int n;
scanf("%d", &n);
int arr[n];
// TODO: n 個の整数値を配列に読み込む
for (int i = 0; i < n; i++) {
scanf("%d", &arr[i]);
}
// TODO: 配列の iterator を作成する
// TODO: has_next と next を使用した while ループで走査する
// 各要素をそれぞれの行に出力する
// 走査ループ内で直接的な配列のインデックス指定を使用しないでください!
return 0;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。