for文の二重ループを一重ループにする方法

一重ループという言葉が正しいのかどうかはともかく、for文の二重ループをfor文一つだけにする方法についてメモとしてJavaScriptのコードで書きます。

プログラマのためのコードパズル ~JavaScriptで挑むコードゴルフとアルゴリズムを呼んで知った方法。といっても、方法は単純なので、やり方を知った今となってはなぜ今まで思いつかなかったんだろうとさえ思う。

2次元配列を探索する場合、たいていの場合は二重ループを使うことが多いと思います。例えば、九九を一の段から順番に探索していって、初めて50以上になった掛け算のみ出力するというプログラムを書こうと思った場合、二重ループを使うと下記のようになります(もっと実用性のあるプログラムを思いつけばよかったのですが、思いつきませんでした・・・。後、見つからなかった場合の判定もしていません)。

// 九九表
var mulArr = [ 
    [1*1,1*2,1*3,1*4,1*5,1*6,1*7,1*8,1*9],
    [2*1,2*2,2*3,2*4,2*5,2*6,2*7,2*8,2*9],
    [3*1,3*2,3*3,3*4,3*5,3*6,3*7,3*8,3*9],
    [4*1,4*2,4*3,4*4,4*5,4*6,4*7,4*8,4*9],
    [5*1,5*2,5*3,5*4,5*5,5*6,5*7,5*8,5*9],
    [6*1,6*2,6*3,6*4,6*5,6*6,6*7,6*8,6*9],
    [7*1,7*2,7*3,7*4,7*5,7*6,7*7,7*8,7*9],
    [8*1,8*2,8*3,8*4,8*5,8*6,8*7,8*8,8*9],
    [9*1,9*2,9*3,9*4,9*5,9*6,9*7,9*8,9*9]
];
var i,j;

for(i=0;i<9;i++){
    for(j=0;j<9;j++){
        // breakの判定
        if(mulArr[i][j]>=50){
            break;
        }
    }
    // breakの判定
    if(mulArr[i][j]>=50){
        break;
    }
}
console.log("掛け算の結果が50以上になるのは、" + (i+1) + "*" + (j+1) + "の時");
    //=> "掛け算の結果が初めて50以上になるのは、6*9の時"

このように二重ループで書いた場合、全く同じbreak文の判定条件文を2回書かなければいけません。しかし、これを一重ループにすることにより、breakの判定条件文を1回だけですますことができます。例えば下記のような感じ。

var mulArr = [ 
    [1*1,1*2,1*3,1*4,1*5,1*6,1*7,1*8,1*9],
    [2*1,2*2,2*3,2*4,2*5,2*6,2*7,2*8,2*9],
    [3*1,3*2,3*3,3*4,3*5,3*6,3*7,3*8,3*9],
    [4*1,4*2,4*3,4*4,4*5,4*6,4*7,4*8,4*9],
    [5*1,5*2,5*3,5*4,5*5,5*6,5*7,5*8,5*9],
    [6*1,6*2,6*3,6*4,6*5,6*6,6*7,6*8,6*9],
    [7*1,7*2,7*3,7*4,7*5,7*6,7*7,7*8,7*9],
    [8*1,8*2,8*3,8*4,8*5,8*6,8*7,8*8,8*9],
    [9*1,9*2,9*3,9*4,9*5,9*6,9*7,9*8,9*9]
];
var i,j;
       
for(i=0,j=0;i<9;j++){
    // breakの判定
    if(mulArr[i][j]>=50){
        break;
    }
    // j+1が配列外参照をしてしまう時
    if(!((j+1)<9)){
        j = -1; // 次のループでjが0になるように、jを-1にする
        i++;    // iをインクリメント
    }
}
console.log("掛け算の結果が初めて50以上になるのは、" + (i+1) + "*" + (j+1) + "の時");
    //=> "掛け算の結果が初めて50以上になるのは、6*9の時"

for文の継続条件はiについて書いているのにたいし、for文の中のカウンタ変数の更新はjのみについて書いています。そうして、iについてはj+1が9未満にならない(つまり9以上)になる時にカウントするようにし、その時jは次のループで0になるように-1に変更します。これでループ一つだけで、2次元配列を探索し、breakの条件判定文も一つだけですむようになりました。

一見、何をしてるのか分かりにくいんですけどね。ただたんにbreakの判定条件を一つだけにしたいなら、関数化するほうが分かりやすいかもしれません。

var mulArr = [ 
    [1*1,1*2,1*3,1*4,1*5,1*6,1*7,1*8,1*9],
    [2*1,2*2,2*3,2*4,2*5,2*6,2*7,2*8,2*9],
    [3*1,3*2,3*3,3*4,3*5,3*6,3*7,3*8,3*9],
    [4*1,4*2,4*3,4*4,4*5,4*6,4*7,4*8,4*9],
    [5*1,5*2,5*3,5*4,5*5,5*6,5*7,5*8,5*9],
    [6*1,6*2,6*3,6*4,6*5,6*6,6*7,6*8,6*9],
    [7*1,7*2,7*3,7*4,7*5,7*6,7*7,7*8,7*9],
    [8*1,8*2,8*3,8*4,8*5,8*6,8*7,8*8,8*9],
    [9*1,9*2,9*3,9*4,9*5,9*6,9*7,9*8,9*9]
];
var i,j;
           
(function(){        
    for(i=0;i<9;i++){
        for(j=0;j<9;j++){
            // breakの判定
            if(mulArr[i][j]>=50){
                return; // 関数を抜ける
            }
        }    
    }
})();
console.log("掛け算の結果が初めて50以上になるのは、" + (i+1) + "*" + (j+1) + "の時");
    //=> "掛け算の結果が初めて50以上になるのは、6*9の時"

2016/10/16追記:
二重ループを抜けるのには、ラベル構文というものを使ったほうがいいかもしれません(JavaScriptで多重ループを抜けるラベル構文について | while(isプログラマ))。

コメント

タイトルとURLをコピーしました