Edited at

JavaScript for文の再設計。for_関数を作りました。


for文がいろいろめんどうと気がついた。

初心者向けのfor文の記事を書いてみました。

JavaScript forループ | プログラミングアカデミー ゼロプラスワン

https://zero-plus-one.jp/javascript/javascript-forloop/

で、あらためてめんどうな事に気がついたのですが、

for (let i = 0; i <= count() - 1; i += 1) {

console.log(i);
}
for (let i = 0, imax = count() - 1; i <= imax; i += 1) {
console.log(i);
}

この二種類の書き方のどっちがいいとか、パフォーマンスがどっちがいいとか、いちいち考えなければいけないことが、うざいなーと思いました。名前はどうでもいいとして、imaxなんていう一時変数を持つのも変な話です。

あとは、二重ループからの脱出は、こんなふうに書かないと実現できないような気がします。違いましたっけ?適当なコードです。

for (let i = 0; i <= iCount()-1; i += 1) {

let breakFlag = false;
for (let j = 0; j <= jCount()-1; j += 1) {
if (i === x) {
breakFlag = true;
break;
}
}
if (breakFlag === tue) { break }
}

あるいは、gotoを使うとか、確かそういう話だった気がします。二重ループを抜けるときだけgotoを使うとか聞いたことがあるようなないようなです。

こういうのは言語仕様から間違っているんじゃないのか、と思ってしまいましたので、forを再設計して実装してみました。

既存のforを上書きはできないので、[for_]という関数で実現してみます。

前に書いた、if関数とか、switch関数のシリーズみたいなもんですかね。

JavaScriptの三項演算子が読みにくいらしいので、if式ならぬ if関数を作りました。そしてもっと読みにくい感。 - Qiita

https://qiita.com/standard-software/items/794724427fcee1cbd00b

JavaScript で if式っぽく動くようなswitch式...というかswitch関数作ったよ。 - Qiita

https://qiita.com/standard-software/items/111a0cfaf0e56d547d70


for_関数の実装

結構簡単でした。

const for_ = (start, end, increment, func) => {

// start, end , increment は整数値とする
// increment は 0 は許容しないとする
// start < end の場合は 1 <= increment
// end < start の場合は increment <= -1 とする

var i = start;
var condition =
1 <= increment
? (i) => (i <= end)
: (i) => (end <= i)
while(condition(i)) {
if (func(i) === false) {
return false;
}
i += increment;
}
return true;
};

構文としては

for_(開始Index, 終了Index, 加算値, ループ時実行関数)

となります。

ループ時実行関数で、return する場合は continue できて return false すると breakして、for_の戻り値に値が伝達されます。

次のように記載できます。 i とか j はループ変数で、二重ループを記載しています。


// 通常のループ
// i が 1,2,3 と変化して、その中で、j が 1,2,3 と変化します。
for_(1, 3, 1, (i) => {
console.log('i', i);
for_(1, 3, 1, (j, result)=>{
console.log('j', j);
});
});

// continue したい場合は return する
// i が 1,2,3 と変化して、その中で、j が 1,3 と変化します。
for_(1, 3, 1, (i) => {
console.log('i', i);
for_(1, 3, 1, (j, result)=>{
if (j === 2) { return }
console.log('j', j);
});
});

// break したい場合は return false する
// i が 1,2,3 と変化して、その中で、j が 1 と表示されます。
for_(1, 3, 1, (i) => {
console.log('i', i);
for_(1, 3, 1, (j, result)=>{
if (j === 2) { return false }
console.log('j', j);
});
});

// 他段階の break をしたい場合は for_ の戻り値を return します
// i が 1 と表示され、その中で、j が 1 と表示されます。
for_(1, 3, 1, (i) => {
console.log('i', i);
return for_(1, 3, 1, (j, result)=>{
if (j === 2) { return false }
console.log('j', j);
});
});

こんな感じで、forの代わりの構文を作ることができるようです。


追加で改良も可能かもしれない。

以前、forLoop関数というものを作ったことがありました。こちらは、二重ループとか考えられてないのですが、ループ時の全てで関数実行か、最初、最後、あるいは中間、で関数をわけてみています。

stsLib.js/stslib_core.js at master · standard-software/stsLib.js · GitHub

https://github.com/standard-software/stsLib.js/blob/master/Source/stsLib.js/stslib_core.js

      _.forLoop = function(start, end,

funcAll, funcFirst, funcMiddle, funcLast) {

c.assert(t.isInts(start, end));
var funcEmpty = function() { return; };
funcAll = t.ifUndefinedValue(funcAll, funcEmpty);
funcFirst = t.ifUndefinedValue(funcFirst, funcEmpty);
funcMiddle = t.ifUndefinedValue(funcMiddle, funcEmpty);
funcLast = t.ifUndefinedValue(funcLast, funcEmpty);
for (var i = start; i <= end; i += 1) {
funcAll(i);
if (i === start) {
funcFirst(i);
} else if (i !== end) {
funcMiddle(i);
}
if (i === end) {
funcLast(i);
}
}
};

こんな改良も行えるかもしれないですね。

いろいろ試して動作確認したりして、楽しむのもよいでしょう。

ご参考にどうぞです。