3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

AtCorder の rep マクロを関数にしたい

Posted at

ことの発端

最近、 paiza で求人をしているのですが、C++ の回答で rep というマクロを多用するコードを見かける。

#define rep(i, n) for (int i = 0; i < (int)(n); i++)

で、よくよく調べてみると AtCorder のサイトで公開されているコードということが分かった。
AtCorder は競プロの世界なので、コーディングの速度だったり、実行速度だったりが目的なので、これはこれでアリかと思う。

ただ、個人的には次の理由でマクロは採用できないかなぁ。

  • 型チェックが行われない
  • デバッガでステップ実行時に、行ズレが発生するケースがある
  • 過去の経験ですっげ~痛い目にあっている(苦笑)

そうは言っても、効率化という大義のためにマクロが登場することがある(5~10年に1回くらい)けど、局所的に、且つ背徳感に苛まれながらマクロを書いてます。

まぁ、正解・不正解がある話じゃないし、マクロの善悪の話じゃなくて、今回はこいつを型安全な関数にできんのかと書いてみたという話です。(処理速度は落ちるであろうことは予測できますが、私は型安全にしたんだ!)

んで、書いてみようと思ったきっかけは、STLalgorithm における反復処理は iterator が登場することが多いので、今回の rep マクロ的なの無かったんじゃないかというのが動機です。 1

書いてみた

#include <iostream>
#include <functional>

void rep(int n, std::function<void(int)> fn){
  for(int i = 0; i < n; i++) fn(i);
}

int main(void){
  rep(5, [](int i){
    std::cout << i << std::endl;
  });
}

そのまま書くとこんな感じかな。。。

あ!!でも

  • break ができない
  • 型をちゃんと渡したい

とか考えて。。。

#include <iostream>
#include <functional>

template <typename T> bool rep(
  T start,  /* 初期値 */
  std::size_t count,  /* 回数 */
  std::function<bool(T)> fn,  /* false を返却したら break */
  std::function<T(T)> stepper = [](T value) {return value + 1;} /* デフォルトは +1 */
){
  T& value = start; /* rename */
  for(std::size_t i = 0; i < count; i++){
    if(!fn(value)) return false;
    value = stepper(value);
  }
  return true;
}

int main(void){
  rep<int>(1, 10, [](auto value){
    std::cout << value << std::endl;
    return true;
  });
  
  rep<std::string>("abc", 10, [](auto value){
    std::cout << value << std::endl;
    return true;
  }, [](auto value){return value + "z";});
}

型推論が効かないのが残念ポイントですが、数値だけじゃなくて文字列も渡せるようになった~
。。。けど、そんなことやるのか? やらないな。
それに、「もう、もはや for 文で良くね?」という感じになったけど、 この関数は break した情報を伝搬する仕様にしたことで、多重ループ時の脱出が楽になった気がする。

int main(void){
  rep<int>(1, 10, [](auto value){
    return rep<int>(value, 10, [](auto value){
      std::cout << value << std::endl;
      if(value == 14) return false; /* 14 が登場したらループ終了 */
      return true;
    });
  });
}

多重ループって、抜けるのが結構面倒だったりするんだけど、これならすんなり抜けられるな。
これはこれで収穫じゃ~
それに、 for 文で範囲指定じゃなくて回数指定というのも地味面倒だったりするので、作ってみたら案外アリかも。
もうちょい煮詰めれば使い物になるかも知れない。

  1. 探せば stl とか boost であるかもだけど、こいういうパーツ作るのを楽しむタイプなので、暫くは探さないことにしよう。メモリ確保しない range 的なのがありそう。

3
2
0

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
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?