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);
}
}
};
こんな改良も行えるかもしれないですね。
いろいろ試して動作確認したりして、楽しむのもよいでしょう。
ご参考にどうぞです。
追記
loop構文を作ってみました。
テストコード側に記述例を載せてます。
https://github.com/standard-software/partsjs/blob/v5.5.0/source_code/syntax/syntax.test.js#L577
loopの先頭(first)か、末尾(last)かの判定や return { break: true }
でのbreakと、多重ループからの脱出のための外ループへの情報伝達なども備えた構文を実現できました。
parts.loop(3)
とか parts.loop(1, 10)
とかparts.loop(100, 200, 10)
という構文にも対応です。
// Break from a double loop
parts.loop(3)((
e, j, array, first, last,
) => {
const loopResult = parts.loop(3)((
e, i, array, first, last,
) => {
console.log(e, i, array, first, last, j);
if (i === 1) {
return { break: true, parentLoopCounter: j };
}
});
if (loopResult.break === true) {
if (loopResult.parentLoopCounter === 1) {
return { break: true };
}
}
});
}