一つ前の記事に対して、C++なんちゃら委員会の江添さんから直々に以下の記事を頂戴した。
やねうらおが本当に必要だったもの(本の虫)
http://cpplover.blogspot.jp/2015/12/blog-post.html
ところで、記事中でやねうらおはCプリプロセッサーマクロを多用している。これは極めて醜悪だ。C++にはlambda式が存在するので、同等のコードはもっと綺麗に書ける。
それは全然綺麗だと思わないな。どこがどう綺麗なんだろうか。
1 2 3 4 5 |
yaneu_for( ZERO, NB, []( auto i ) { // 処理 } ) ; |
まず、綺麗か綺麗でないか以前に、そのコードを見る限り、江添さんは正しく元の記事の内容を理解してない。そもそもZEROとNBを毎回指定したくないという話なんだよ。
enum Square { SQ_ZERO = 0, ….. , SQ_NB = 81 }; があって、
for(auto sq : SQ) {… }
のように書きたいわけだよ。ラムダ式を使って書くなら、
1 2 3 4 5 6 7 8 9 10 11 12 |
template < typename T, typename Func > void yaneu_for(Func func) { for (T i = T::ZERO; i != T::NB; ++i) { func(i); } } こうして yaneu_for |
こうだろう。
それで、
for(auto sq : SQ) {… }
と
yaneu_for<Square>([&](auto sq) { … });
とどちらが綺麗かという話をしている。後者のコードは、汚すぎて目が潰れそうだよ。私が本当に欲しかったのは決してそんなものではないんだよ。syntaxは前者でいいんだよ、前者で!
上の2つのsyntaxを提示されて、誰が後者で書きたいと言うのだ…。百歩譲って「前者で書くためにはdefineを使わないといけないから仕方なく後者を使う」というのならわからなくはないのだが…。
ともかく、enumのほうはともかく、Bitboardのほうは本来range based forで書くべきものではない&ラムダ式で書く以外の代替手段も見当たらない(ラムダ式で書くのは今のコードより可読性が落ちるので私は採用したくない)という結論みたいなので安心しました。江添さん、戀塚さん、ありがとうございました。
range-based for は基本的に、
sequencial container に対して同じ処理を適用するコードを、
より簡便かつ安全に書けるようにしよう、
というシンタックスシュガーなので、
今回の場合「普通に従来の for 使え」って感じだと思います。
しいて言えば、型安全性や名前のスコープの関係から、
enum よりも enum class ( scoped enum ) を使い、
ループの際には static_cast を使うべきかも知れませんが。
まあ、そういうことみたいですね。
Bitboardに対してfor(auto sq : target)のように書けるのは大変便利なのでやねうら王miniでは、このコードは残しておくつもりですが。
すみません、 Boost.Range の boost::irange 使え、
という意見を聞いたので、
それだけお伝えしておきます。
boost.irangeって単に開始と終了とstepを指定して、そのrangeオブジェクトが得られるだけなのでは。
Bitboardのforには使えませんし、enumに対してなら、このコメント欄の下のほうにある白山さんのコメントにあるコードのほうが優れているような…。
構文解析(「やねうらおが本当に必要だったもの」は必要ではない)
C++なんちゃら委員会・・・通常、文法を決める~委員会は、「その世界」の「神」である。
「神」には逆らってはいけないーー>逆らうと地獄に落ちる。
結論:C++なんちゃら委員会に逆らうと地獄に落ちる。
派生結論
「C++の世界」の美的判断はC++なんちゃら委員会の基準に従うこと。
ただし、この「C++の世界の美的判断基準」がより広い一般世界で通用する・・・という保証はどこにもない。
要するに「好きにしてよろしい」・・・という訳ですよ。
昔の人も言っておりました。
「C++とハサミは使いよう」でありますれば、、、。
ここの議論の進展状況を見ていると、勉強になりますねえ。
・・・というよりは、実は面白いのではありますが。
いろいろな立場の方がそれぞれのスタンスでコメントができる。
まことに貴重な場所でありまする。
うぅ。火種を注いでしまったか。
妙な論争になりませんように。
この記事と直前の記事は「range-based for ってどんなもんなの?」っていうのを確認しただけっていう認識でよいのでしょうか。
> これをforで書けるようにしたいだけなんだよ。なんで20年前からdefineマクロで出来ることがいまだにforで書けないんだよ。C++なんとか委員会はこの20年間、何やってたんだよ。
と書いていますが、やねさんは困ってないんですよね?マクロ定義で偽の for 構文作れば済むんですよね?
それともマクロ定義だと都合が悪くて range-based for (とラムダ式)が便利っぽかったけどやっぱりダメだったから困っていると言っているのですか?
単に、やねうらおさんが望む range-based for と、C++の設計思想が合致しなかったということなのでは?
> 単に、やねうらおさんが望む range-based for と、C++の設計思想が合致しなかったということなのでは?
はい。そういうことです。でもdefineを使えば、range-based forが使えるというhack(?)を発見しました。そしてそうすると大変美しいsyntaxで書けるということを記事にしました。
でもわずかにパフォーマンスが下がっているかも知れないので、速度が要求されるところでは使わないかも、という状況です。
range-based forはコンピューター将棋で使えるのか?
結論:一部使えるが、一部使えない。
経緯:
>・・・書けるらしいという噂を聞きつけ、定義してみた。
もともと「噂の再確認作業」と認識しております。
そうしたら思惑通り動いていない事がわかった、、、と
それで、ユーザークレームのようなものを出してみた。
>せめて次のように展開して欲しかったなぁ…。
希望していた反応は
>C++のエキスパートが「それ、XXXで出来るよ」・・・
であったが、残念ながらメーカー側の回答は
「お客様、残念ですがその使用方法はサポートしておりません。」
というものであった、、、という状況報告。
以上の理解で間違っている所ありましたら、訂正をお願いします。
結論のところ、ちょっと補足
>enumのほうはともかく、Bitboardのほうは本来range based forで書くべきものではない&ラムダ式で書く以外の代替手段も見当たらない・・・
https://twitter.com/yaneuraou/status/677849316577284097?ref_src=twsrc%5Etfw
補足の補足
>・・・速度低下はほとんどないかも知れないが…よくわからないので誰か実験してコメントください。
動くには動くのだけれど、速度低下が起きている可能性がある、、、と。
だからF1には確認しないと使えない。
でも便利だ。
従って、
>Bitboardに対してfor(auto sq : target)のように書けるのは大変便利なのでやねうら王miniでは、このコードは残しておくつもりですが。
と、こうなる訳か。
> マクロ定義で偽の for 構文作れば済むんですよね?
一応、マクロ定義で解決してるんですけど、若干パフォーマンスが落ちているかも知れないので、もっとofficalの機能でdefineというような醜悪なものを使わない方法があるのかと思ったら、defineは使っていないけどもっと醜悪なsyntaxのものを綺麗だと言ってなんちゃら委員会の人から提示されたでござる。という結末。
これくらいでどうでしょうか。
http://ideone.com/914Iia
ありがとうございます。それがほぼ模範回答だと思うのですが、私のソースコードではできればenum structを使いたくないのです。駒ひとつひとつについてPIECE::PAWN , PIECE::LANCEのように書くのはソースコードが非常に見難くなるので。(Stockfishもそうなっていまして…)
つまり、
enum Piece { PIECE_ZERO , … , PIECE_NB };
enum Square { SQ_ZERO , … , SQ_NB };
…
のように定義してある状況です。
下のコメントにあるmelponさんの実装のほうが好ましいです。
前の記事と今回の記事を拝見したのですが、実際のところ、やねうらおさんが本当に欲しかったものって、こういうことですかね?
http://melpon.org/wandbox/permlink/bGswE6N1sy0djX5R
江添さんが嫌いな汚いプリプロセッサを使うこともなく、range-based-forの仕様に従って求めている挙動をするようになってるはずだと思います
はい、それは完璧です。enumのほうに関しては、それで代替できますね。さすがmelponさんです。
Bitboardのほうも同じようにitereratorを返せばrange based forに適合するようには書けそうですね。
行数増える = 理解しにくくなる ことによるデメリット のほうが defineを使うことによるデメリット を上回ると私は考えてますが。
あ、えっと、wandboxを使用させてもらっているだけで、私はmelponさんとは無関係ですよ。通りすがりの無名の趣味グラマです
おお、そうなのですか。
http://melpon.org/wandbox/permlink/8dBf1Lcc2hwaOBGK
範囲for文がC++11で入るときにboostのrangeライブラリも標準入りするとかなんとかとかいう話も聞いた気がしたんだけど入らなかったのよね
boostのrangeライブラリ入れるぐらいなら、Pascalのような部分範囲型を導入したほうが綺麗に書けるようになると思うんですよね。20年ぐらい前にその議論があったときは、C/C++は速度と移植性がすべてで、そういうsyntax sugarみたいなものは一切要らない、みたいな空気があったんですけど、いまはそうでもないと思うので、PODの見直しからやって欲しい気はしますけどね。
正直あんまり知識ないのでなんとも言えないのですが(逃げ)ライブラリで十分に実現できていることに対して過去のコードやCとの互換性を壊す(かもしれない)ような変更をすることはまずないでしょうね。
PODに部分範囲型を導入しても過去のコードやCとの互換性は何も崩れませんよ?