この記事はプログラミングの基本的な要素について言語化する試みです。今回は反復について説明します。
どういうときにどういうプログラミングの機能を使うのが良いのか、機能の特徴を整理します。
反復とは
反復とは条件を満たすまで何度も同じ処理を繰り返すプログラミングの制御構造です。
構造化プログラミングでも、プログラムを構成する3つの制御構造の1つとして挙げられています。
決まった処理を何度も高速に実行できることはプログラミングの強力な特長なので、反復は重要な概念です。
また、写像(map)、抽出(filter)などのコレクション操作メソッドも反復を利用して実装されます。
反復の形式
反復処理を記述するために多くの言語で for, while という制御構造が提供されています。
JavaScript(for文)
var sum = 0;
var last = 5;
for (var i=0; i < last; i++) {
sum += i;
}
JavaScript(while文)
var sum = 0;
var last = 5;
var i = 0;
while (i < last) {
sum += i;
i++;
}
for文は初期化式、終了条件式、更新式 がfor文に含まれています。while文は終了条件式のみが含まれます。
反復の構造は以下の部品から成り立ちます。
A) 初期状態の定義(初期化式)
B) 繰返したい処理
C) 状態の遷移(更新式)
D) 終了条件を満たしているか判定(終了条件式)
プログラマーにとっては B) 繰返したい処理 が最も関心のある部分で、あとの A) C) D) は反復に
必要なので書く部分です。
ただ、A), C), D) の条件が間違っていたり、書き忘れると無限ループに陥る危険があるため、
慎重に記述する必要があります。
for文はこれらを書き忘れないように強制するため、問題を起きにくくする点で効果があります。
コレクションの反復
配列、リストなど、コレクションオブジェクトに含まれる要素に対して繰返し処理を実行するタイプの反復の記法も存在します。
JavaScript
var src = [1, 2, 3, 4, 5];
var sum = 0;
for (var i of src) {
sum += i;
}
Kotlin
val src = listOf(1, 2, 3, 4, 5)
var sum = 0
for (i in src) {
sum += i
}
println(sum)
Ruby
src = [1, 2, 3, 4, 5]
sum = 0
src.each {|i|
sum += i
}
先に紹介した初期化式、更新式、終了条件式を含んだfor文に比べて、コレクションの反復機能は無限ループに陥る危険が無い点でとても効果があります。
特に避ける理由がない限り、コレクションの反復機能が使える場合は使ったほうが良いでしょう。
コレクション操作メソッドを使って反復を書く
言語から提供された反復機能でなく、コレクションオブジェクトが持つ操作メソッドを使って反復を書くこともできます。
JavaScript
var src = [1, 2, 3, 4, 5];
var sum = 0;
src.forEach(i => {
sum += i
});
プログラミング言語で提供された反復機能とコレクションオブジェクトが持つ操作メソッドの大きな違いは、コレクション操作メソッドでは繰り返しの途中で中断(脱出)ができないことです。
for文だと以下のようにbreakを使って繰り返しを中断することができますが、コレクション操作メソッドであるforEach() では同等のことができません。
JavaScript
var src = [1, 2, 3, 4, 5];
var sum = 0;
for (var i of src) {
sum += i;
if (i > 2) {
break
}
}
途中で反復処理を中断したいケースとしては、コレクションの中に最低1個だけ条件に合うものが見つけたい処理などがあてはまるでしょう。
ただし、Rubyなど一部の言語では、操作メソッドを使った場合でも脱出が可能なように言語が設計されています。
このような言語なら上記の制限は発生しません。
反復を使うべき場面
反復は基本的な制御構造です。用途が制限されていないために様々な処理に使えますが、汎用的すぎるので何をしている処理なのか読み解くのに時間がかかるデメリットもあります。
例えば上の合計値を求める処理であれば、KotlinやRubyにはコレクションの合計値を計算するメソッドが用意されていますので、そちらを使うほうが可読性が高くなり、処理の意図も伝わりやすくベターです。また、意図違いのプログラムを書く危険も減ります。
Kotlinでsumメソッドを使った例
val src = listOf(1, 2, 3, 4, 5)
val total = src.sum()
println(total)
コレクション操作メソッドを使わずに反復処理を書くのは、うまく使えるコレクション操作メソッドがない場合など、明確な理由がある場合に限定するほうが良いでしょう。
また、コレクション操作メソッドを使わない理由として、コレクション操作メソッドの機能を知らないというケースもあると思います。もしチームメンバーがこの理由でforやwhileをやforEach() など反復機能を使っているのだとしたら、コレクション操作メソッドの機能をチームで共有したほうが良いでしょう。そして使える場面ではコレクション操作メソッドを使うほうがベターだよねと認識共有しておくと、メンバー間のコーディング品質を揃えるのに役立つでしょう。
京都オフィス勤務のエンジニア。アジャイル開発のエキスパートで、お客様とのコミュニケーション能力も高く、ファシリテーターとして各方面で重宝されているため多忙を極めている。