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


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

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

JavaScript forループ | プログラミングアカデミー ゼロプラスワン
https://zero-plus-one.jp/javascript/javascript-forloop/

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

```js
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なんていう一時変数を持つのも変な話です。

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

```js
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_関数の実装

結構簡単でした。

```js
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 はループ変数で、二重ループを記載しています。

```js

// 通常のループ
// 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

```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.js#L218

テストコード側に記述例を載せてます。
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)`という構文にも対応です。

```js
          // 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 };
              }
            }
          });
        }
```