LoginSignup
2
0

More than 3 years have passed since last update.

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

Last updated at Posted at 2019-01-13

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 };
              }
            }
          });
        }
2
0
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0