TNGとは?

この記事は今後、大きく加筆・修正される予定です!!

(中の人は学祭当日未明、この記事を書いています…

 

「TNG」はTNPのゲームを一括管理・公開するために作られたランチャーのような何かです。同じ役割を担っていた、先代の「ゲームジェネレーター」の改良版として、一年前から制作されてきました。(ただし今もバグまみれの模様)

制作は私サバと、現会長のコショウ氏が主に行い、アイコンは蟹氏に描いていただきました。イメージは魚のタナゴだそうです。(TNG+魚要素=タナゴ)

ちなみにTNGは「Tnp’s Next generation Game launcher」の略です。さらに展開すると、「The next generation programmer’s Next generation Game launcher」という php のような再帰的頭字語みがある気がして気に入ってます。

 

AtCoder Beginner Contest 112

こんばんえるえる〜〜〜〜
ガナリヤです
大学の後期がついに始まりましたね
今週一週間は毎日しんどくて未だに体の疲れが取れません。
コミュニケーション能力を付けないとこのままではまずい気がします。

今回は、AtCoderBeginnerContest112に出ました。
結果としては、ABCD4完は出来ましたが、Cで4WAしているのでもうちょっと細かい部分に意識をしないといけませんね・・・
早速内容を振り返っていきましょう!


A 「Programming Education」

問題文

AtCoderはクソデカ年商となり、プログラミング教育をするようになった。
入力から自分の年齢Nが入力される。
N=1なら”Hello, World”と出力せよ。
N=2ならA+Bの答を計算せよ。

解法

やるだけです。
もう20秒ぐらい早くとけるようになりたいです。


B 「Time Limit Exceeded」

問題文

Xさんは、外から家に帰ろうとしています。
現在位置から家に帰るルートは、N個存在しますが、それぞれに係る時間tiと、かかるコストciがあります。
時間T以内に、帰らなければいけない時、最小のコストを求めてください。

解法

時間T以内なので、時間T以内のルートのみを列挙して、さらにその中でも一番コストの低いものを出力してしまえばいいです。
気をつける点としては、ルートが一つもない場合はTLEと出力しないといけないので、最初の変数の初期化で上限いっぱいにしておいて、値が更新されてなければTLEとしないといけません。
実装に2分もかかったので半分ほどにするひつようがあります。

コード


int main() {
 
    LL N, T;
    cin >> N >> T;
 
    LL cost = LONG_LONG_MAX;
    for (int i = 0; i < N; i++) {
        LL c, t;
        cin >> c >> t;
        if (t <= T) {
            S_MIN(cost ,c);
        }
    }
 
    if(cost == LONG_LONG_MAX){
        OUT_L("TLE");
    }else{
        OUT_L(cost);
    }
 
    return 0;
}


Pyramid

問題文

問題文長すぎるのでここを見て→ https://beta.atcoder.jp/contests/abc112/tasks/abc112_c

解法

内容としては簡単だったのですが、3つ特殊ケースがあり、そのケースに気づくまで時間がかかってしまいました。

制約を見るとCxもCyも0~100以内なので、全探索しても余裕で間に合うことが分かります。
中心のCx、Cyを仮に全探索してみます。
すると、中心の高さHを求めたいわけですが、この高さは他の点1つを選んでしまえば逆算することが出来ます。
そうして求めたHと他の点を全て比べて、条件を満たせばそれが答えになります。

しかし、この解法だけだとうまくいかない場合があり、参考にする点が高さ0の場合です。
高さ0の場合、もともと高度は(H -|X – Cx| – |Y – Cy|, 0)のため、マンハッタン距離で0だったのか、最初から0だったのか判断することが出来ません。
よって、関係ない答を出力してしまう可能性があります。
よって、そのケースは省いて計算する必要があります。
体感的にBレベルでしたが、特殊ケースだけはDレベルな感じがしました。

コード

int main() {
 
    int N;
    cin >> N;
 
    VLL x(N), y(N), h(N);
    REP(i, N) cin >> x[i] >> y[i] >> h[i];
 
    for (LL i = 0; i <= 100; i++) {
        for (LL j = 0; j <= 100; j++) {
            LL H = 0;
            for (LL k = 0; k < N; k++) {
                if (h[k] != 0) {
                    H = h[k] + ABS(i - y[k]) + ABS(j - x[k]);
                    break;
                }
            }
            if (H <= 0) continue;
            bool good = true;
            for (int k = 0; k < N; k++) {
                LL _h = C_MAX(H - ABS(i - y[k]) - ABS(j - x[k]), 0LL);
                if (_h != h[k]) good = false;
            }
            if (good) {
                cout << j << " " << i << " " << H << endl;
                return 0;
            }
        }
    }
 
    return 0;
}


D Partition

問題文

整数N, Mが与えられます。
a1 + a2 + ・・・ + aN = Mとなるような長さNの数列aにおいて、a1~aNの最大公約数となる最大値を求めてください。

解法

Mが与えられるので、可能性のあるaの組み合わせを作成し、その最大公約数を求める必要があります。
最大の最大公約数であるため、少し工夫しないといけません。なぜなら、制約でNが10万のため、計算量的にNlogNが最大であるためです。

そこで、最大公約数について一度考え直して見ます。
最大公約数は、a全てにおいて、共通の約数を持っており、共通の約数ということは同じ倍数を持っているということに等しいです。
共通の倍数をKとすると
Kb1 + Kb2 + ・・・ + KbN = Mということになります。
つまりM = K * (b1 + b2 + ・・・ + bN)というわけで、Mは因数としてKを持つことが分かります。
よって、Mに対して、約数を列挙し、その約数の中で、NをかけてもMを超えないものが答となります。
約数をTとして、T
N < Mのとき、数が余らないか?というう話になりますが、Mも因数Kを持つため、うまく配分すれば全ての変数がKを因数に持つことが分かります。

素因数分解はO(√N)でできるので、計算量的にも間に合うことが出来ます。

今回のDは3分で通せたので、成長を感じます。(なお、C)

コード



//nの約数
template<typename T>
vector<T> DIVISOR(T n) {
    vector<T> v;
    for (LL i = 1; i * i <= n; ++i) {
        if (n % i == 0) {
            v.push_back(i);
            if (i != n / i) {
                v.push_back(n / i);
            }
        }
    }
    sort(v.begin(), v.end());
    return v;
}
 

int main() {
 
    LL N, M;
    cin >> N >> M;
 
    auto divisors = DIVISOR(M);
    LL maxV = 1;
 
    for (int i = 0; i < SZ(divisors); i++) {
        if(divisors[i] * N <= M){
            maxV = divisors[i];
        }
    }
    OUT_L(maxV);
 
    return 0;
}
 
 

### 感想

今回の問題は、Cが一番難しかった感じがあります。
4WAは流石に出しすぎなので、もうちょっとミスを減らさないといけませんね・・・

ガナリヤでした!

C++が使えたい 項目7(現行のC++への移行 – 1)

サバです。
3章にはいります!

今回も、表とその下の解説は無駄に長いので、適当に流し読みしてください!

 

 

<以下長い注意>
ここの内容は、サバが適当な自己解釈を重ねた結果、攪拌され白濁してしまった知識の泉から、沈殿物を素手で持ち上げようとした結果です。保証はしかねます。ご了承ください。

内容はEffectiveModernC++をわざわざ書き写したような何かです。
題名にEffectiveModernC++が入っていないのは更新が途中で止まる自信があるからです。

また、もしよろしければ、間違い、気になったところ、分かりづらいところなどを指摘してもらえると、サバがピチピチ跳ねて喜びます。喜びすぎてプログラミング言語C++第四版をあなたに投げつけるかもしれません。
(ぜひ教えてください。よろしくお願いします。 < ( _ _ ) > 何でもするとは言いませんから…

 

 

 

項目7 オブジェクト作成時の()と{}の違い

結論:{}を使うならstd::initializer_listコンストラクタに気をつける

 

C++11はオブジェクト作成時の初期化に(){}を使い分けられます。ただし、組み込み型(プリミティブ型)変数の初期化には = も使えるため、初期化代入がごっちゃに理解されることが多いようです。自分もそうでした。

 

 

 

 

・初期化方法まとめ

重要なのは組み込み型の = 初期化特殊であることです。
ここではユーザ定義型(構造体・クラス)の例として Widget という型名を用います。

組み込み型
(クラス・構造体以外)
int Integer1 = 0;
int Integer2( 0 );
int Integer3{ 0 };
すべて同じ意味
0 で int 型変数を作成・初期化
= でも初期化をすることができる
初期化であって代入ではない
ユーザ定義型
(クラス・構造体)
Widget widget1( 0 );
Widget widget2{ 0 };
すべて同じ意味
Widgetのコンストラクタに 0 を渡して、Widgetオブジェクトを作成・初期化
  • ここにでは省略しましたが、{} の代わりに = {} も使用できます。全く同じ意味です。
  • ちなみにユーザ定義型で、下コード widget5 のように初期化をしない場合、デフォルトコンストラクタによって暗黙的に初期化されます。個人的には初期化を忘れたバグの根源なのか、デフォルト初期化したいのかが分からないので、widget6 の明示的初期化が良いと思います。
    Widget widget5;
    Widget widget6{};
    どちらもデフォルトコンストラクタで初期化

  • コンパイラによっては1変数を受け取るユーザ定義型のコンストラクタなら = でも初期化できるようです。

 

 

 

 

 

・初期化の統一記法

C++は変態なので変数を初期化する場所・場合がたくさんあります。
そんな中でも {} だけは統一的にすべての初期化を行うことができます。{}すんごい!!

組み込み型 int integer1 = 0;
int integer2();
int integer3{};
= () {} 全てで初期化できます。
全てデフォルト値0での初期化です。
ユーザ定義型 Widget widget1();
Widget widget2{};

// NG : 二度手間
Widget widget3 = Widget{};

= では期待通りの初期化はできません。一時オブジェクトを作成し、ムーブコンストラクタでムーブ初期化するという二度手間になります。
(最適化されなければ)
宣言初期化子 class Widget
{
    int member1 = 0;
    int member2{};

    // メソッド?
    int member3();

};
() では初期化できません。
コンパイラにとってはメソッド
(メンバ関数)宣言にしか見えません。
コンストラクタ初期化子 class Widget
{
    int member1;
    int member2;

public:  
    Widget(int value)
        : member1{ value }
        , member2( value )

    {}
};

= では初期化できません。
メンバを初期化するためのもので、代入するためのものではありません。(適当に理由を付けました)
コピーできないオブジェクト std::atomic<int> ai{ 0 };

// Error : 関数は削除されてる
std::atomic<int> ai =
        std::atomic<int>{ 0 };

コピーやムーブを出来ないように設計されたオブジェクトは = 等を使えません。

左の例ではムーブコンストラクタが消されているため、エラーとなります。

template
との組み合わせ
template<typename T> decltype(auto) func()
{
    // どっち?
    return T::Whats();
}
こんな状況で () 初期化を使うのはやめましょう。Whats という static 関数呼び出しにしか見えません。
このコードで Whats オブジェクトの初期化をしていた時は、
バグの発見に大分骨を折りそうです。
  • 上の例を見ると {} だけいつでも初期化に使えることが分かります。

 

 

 

 

 

 

・ {} 初期化の型チェック機能

{} 初期化は統一記法以外にも型チェックという利点があります。
縮小変換(精度が落ちる変換)が必要な初期化が行われた場合、コンパイラがエラーを出してくれます。(コンパイラによっては警告)

int  pi { 3.14 };              // Error

double
  x{}, y{} z{};
int  sum { x + y + z };    // Error
 double 型から int 型への暗黙的な縮小変換
 (精度が落ちる・値が壊れるような型変換)
bool flag { 2 };               // Error int 型から bool 型への暗黙的な縮小変換
  • エラーを避けるためには static_cast を使いましょう
    int pi{ static_cast<int>( 3.14 ) };

 

 

 

 

 

・{} 初期化の落とし穴

{} は凄い!ですが、致命的な問題があります。
それは std::initializer_list との相性問題です。
std::initializer_list を取るコンストラクタを持つオブジェクトを {} 初期化すると、縮小変換をしてでも、力ずくでこの std::initializer_list コンストラクタを使おうとします。

class Widget
{

    // コンストラクタ1
    Widget( int value )
    {}
    // コンストラクタ2
    Widget( std::initializer_list<bool> list ) 
    {}

};

int main()
{
    // コンストラクタ2を呼び出す
    Widget w{ 100 } // 縮小変換 Error も

    return 0;
}

main 関数内で、Widget の
コンストラクタ1
を呼び出しているように見えますが、
コンストラクタ2
が呼び出されます。
 

コンストラクタ1 を呼び出すには () を使った初期化をする必要があります。
 

また、{} 初期化は型チェックを行うため、この場合 Error や Warning が発生します。

  • コンパイラには {} が std::initializer_list にしか見えないのでしょうか…
  • 力ずくで変換できない場合、つまり、暗黙の型変換方法がない場合は、こんなことになりません。 例えば、std::initializer_list<bool> ではなく、 std::initializer_list<std::string>であった場合は、int 型から std::string 型への暗黙の型変換方法がないため、コンストラクタ1 が呼び出されます。

 

残念ながらこの相性問題の代表例は STL です。
std::vector も std::initializer_list をとるコンストラクタを持つため、こんな頭痛が痛い問題が起きます。

// { 10, 1 }
std::vector<int> vec1{ 10, 1 };
要素数2で、それぞれの値が 10, 1 となる
std::vector を作成
// { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
std::vector<int> vec2( 10, 1 );
要素数10で、すべての値が 1 となる
std::vector を作成
// { 100 }
std::vector<int> vec3{ 100 };
要素が 100 のみで要素数1となる
std::vector を作成
// { 0, 0, 0, … ( x 100) }
std::vector<int> vec4( 100 );
要素数100で、すべての要素をデフォルト値( 0 )で初期化した std::vector を作成
  • std::vector は {} だけじゃすべての初期化ができない… {} は初期化の統一記法じゃなかった!!!

 

初期化するオブジェクトに std::initializer_list をとるものがないか事前に確認しましょう。std::initializer_list をとるコンストラクタを定義するときは本当に必要なのか考えましょう。

 

 

 

 

 

 

Effective Modern C++ の中では、(), {} どちらの初期化も長所・短所を持つため、いずれかの記法を優先して使うように決め、必要に応じてもう一方を使おうとまとめられています。

 

 

 

 

< 前の項目へ   次の項目へ >  // そのうち…

コミケ2018夏 お品書き

特設サイトはこちら←


2018コミケ夏!来る!

こんばんえるえる〜〜〜〜。
TNPのWeb担当ガナリヤです!(ガナリヤはWeb言語が言うて得意ではない。誰か変わって・・・)

今日は2018夏コミックマーケットの、TNPの出品作品を見ていこうと思います!
特設サイトは上記のリンクにありますので是非!

 

表紙

まずはこちらの表紙ですね!
二年次の蟹さんの作品です!

かわいい!!!!!
これまでのTNP子ちゃんよりも、デジタルでVTuberっぽい作風となっています。
これはもうジャケ買いする人がでてくることでしょう・・・(中身でも勝負しろ(迫真))

今回のTNPBOXのTNP子ちゃんがVTuberっぽい作風になったのは訳があります。現在二年次のうおちーさんが、TNP念願のTNP子VTuber化を図って躍進してくれています。TNP子のVTuberデビューも近い・・・?
そして、そのVTuberを意識して蟹さんにイラストを書いていただきました!
これまでのTNP子になかった猫耳属性や、スカート姿など、新たな新生TNP子が垣間見えた気がします!

表紙が可愛いのは認める。ただ、表紙だけほしいっていう声はゆるさない!

 

UniPyon

ゲーム紹介に入っていこうと思います。
まずはこちらの作品「UniPyon」。製作者は我らが部長こしょうです。

こちらのゲームでは、Unityちゃんを操作してウニティを中央のゴールにゴールするといったゲームです!
僕と同じくコミケ終盤で一気に詰め込んだゲームながら、クオリティは素晴らしく、とくにグラフィック面はもうただただすごいことになってます(動画でみせたい)!
音楽に合わせてウニティや波形が動いたり、発光を上手に使っているなど、とても楽しい雰囲気になっています!

ただ、僕が一番好きなのはこの部分。

タイトル画面がわかりやすい!そして、タイトルで遊べる!!!これ大事な要素です!
タイトルで遊べるゲームは神ゲー。はっきりわかんだね。

非常に素晴らしい作品になっています。
是非ご試遊ください!

  

JumpingBox

次はこちらの作品「JumpingBox」。
製作者は、二年次うおちーです。(二年次のほうが三年次より作品を多く作っている・・・うっ頭が・・・)

ゲームライブラリはSiv3D!
うおちーもSiv3Dの便利さに気づいてしまったようです。
ゲーム内容としては、自動生成される足場を踏んでいきながら、落ちないようにできる限り上に登り続けていくというゲームです。
こういうシンプルなゲームはつい、やり続けてしまうという魔性の力があります・・・(3敗)
来年は、老害ながら、二年次がものすごく優秀なので何も心配がないです。(老害ムーブ(三年次もがんばるぞ))

   

UFOキャッチャー

続いてはこちらの作品「UFOキャッチャー」。
製作者は、二年次yutaくんです(強すぎぃ!)。

こちらのゲームはUnityでできており、3Dゲームでの参戦です。
ゲーム内容は、アームでグッズをふんっ!ってやる、そうUFOキャッチャーそのものです!
Unityでこういった物理演算をやるのは非常に難しく、スクリプトもだいぶ増えてしまうため大変なのですが、素晴らしい出来になっています。
モードも複数用意されており、練習モードで鍛え上げてから本戦モードに望むことができます。

そして、一番僕が好きだったのが、発表の際の物理演算バグです。
やっぱり、3Dゲームは時たまにバグってしまうのが最高なんだなと勉強になりました。
僕も制作活動頑張ろうと思います。(2年次にも1年次にも負けられない!)

 

Just Do It!

続いてはこちらのアプリ?「JustDoIt」
製作者は、ガナリヤ(僕)です。

JustDoItにはまり、パソコンのスリープを解除したときに自動でJustDoItおじさんの声が流れると勉強頑張れるんじゃないか?と思って作りました。

ライブラリはElectronを使用しており、解説はhttps://qiita.com/ganariya/items/6c9ca69ca1c89282c228に書いてます。こちらのページからダウンロードもできます。

TNPはゲームだけと思われがちですが、イラスト・音楽、そしてアプリ制作もあるんだぞ!
というところも見せていきたいです。


        
    

以上が今回の作品となり・・・

・・・あの作品を忘れているじゃないか!
   

TNG

自分がMacでTNGを起動できなかったため画像はお見せできないのが悲しいのですが・・・
こちらのTNGは、三年次のサバとこしょうの共同制作になっています。

こちらの作品TNGは「TNP Generator」 の略であり、ゲームでなく、アプリとなっています。
使用ライブラリはUnityであり、TNPの作品をデータベースに登録し、検索をすると自動で起動してくれるという代物です!
ソースコードは五万行を超えているとかなんとか・・・(すごい(絶句))
今回の配布はTNGも入っているらしく、サバとこしょうの一年間の集大成が詰まっています。特に、サバはほぼ毎日こちらの作品に力を注いでおり、一時期はSiv3Dで実装していましたが、ライブラリの関係上、一度すべてUnityに移し、更に開発を進めていました!(すごい(悶絶))
 
今回のTNP_BOXの中で、一番やばい作品だと思います!
是非!明日のコミックマーケットで触っていただければと思います!やばいです!!!!!!!!

 

まとめ

以上が、今回の夏コミのTNPBOXのお品書きになります!
非常に濃厚な中身になっており、特に二年次の躍進、そして、サバ・こしょうの血の結晶が垣間見えたTNPBOXになっていると思います。

是非明日はTNPBOXを手にとっていただければと思います。

明日 8月10日西地区よ-24aにて、TNPBOXとともにお待ちしています!!!!!!

C++が使えたい 項目6(auto – 2)

サバです。
autoについては最後の項目です(まだ二つ目)

 

<以下長い注意>
ここの内容は、サバが適当な自己解釈を重ねた結果、攪拌され白濁してしまった知識の泉から、沈殿物を素手で持ち上げようとした結果です。保証はしかねます。ご了承ください。

内容はEffectiveModernC++をわざわざ書き写したような何かです。
題名にEffectiveModernC++が入っていないのは更新が途中で止まる自信があるからです。

また、もしよろしければ、間違い、気になったところ、分かりづらいところなどを指摘してもらえると、サバがピチピチ跳ねて喜びます。喜びすぎてプログラミング言語C++第四版をあなたに投げつけるかもしれません。
(ぜひ教えてください。よろしくお願いします。 < ( _ _ ) > 何でもするとは言いませんから…

 

 

項目5 auto が期待とは異なる型を推論する場面では ETII を用いる

 

結論:autoは明示的キャストと併用しよう

ETIIはC++的で明示的な型キャスト、static_cast を使ってauto変数を初期化する方法です。

auto itsInteger = static_cast<int>(64 * 0.2);    // キャストでint型変数として推論させる。
  • これを使ったらauto使えるし、どんな型と目的でキャストしてるのか明示的でわかりやすいよね!って話です。

 

また、std::vector<bool> を使うときにこの操作が必要な場合があります。
というのもstd::vector<bool>の [ ] 演算子は bool& ではない変な型を返してくるからです。

std::vector<int> vecInt = {1, 2, 3};
decltype( veclnt[0] );
auto front = vecInt[0];
decltype → int& // 分かる。内部要素への参照。
front       → int   // 期待通り。
std::vector<bool> vecBool = {true, true};
decltype( vecBool[0] );
auto front = vecBool[0];
decltype → std::vector<bool>::reference // WTF
front       → std::vector<bool>::reference // WTF

うーんこの…。そこでこの std::vector<bool> 使う時だけETIIしよって話です。

std::vector<bool> vecBool = {true, true};
auto front = static_cast<bool>( vecBool[0] )
front → bool   // 期待通り
  • もちろんbool型以外のstd::vectorはこんなことないです。ふつうです。こいつが変態なだけです。
  • めんどい…
  • 今にでもstd::vector<bool>に殴り掛かれそうなら、殴り掛かる前に下の内容を読んでみてください。

 

 

 

・深淵を覗きに行くために…

std::vector<bool>はうん○こ。ではなくちゃんと理由があるみたいです。

bool型のベクターはメモリを節約するため、内部でビット演算を使用して値を保持しています。となると、要素一つあたりのサイズは1ビット。でもC++の参照が指し示すことができるのはバイト単位…(参照は内部的にはポインタであるため)あれ? ビット単位な各要素への参照ってできないじゃん。じゃあ参照受け取って、各要素の値を直接変更するとかもできないじゃん! って当然なるわけです。

そこでstd::vector<bool>::reference君の登場です。下記のコードのように、ぱっと見いい感じに動作するよう彼は実装されています。この書き方だけならいつもの通りです。

std::vector<bool> vecBool = {false, false};
vecBool[0] = true;
vecBool[1] = true;
{false, false} // vecBool の中身
{true, false}
{true, true}

 

しかし、上記の注意点だけでは終わらず、追加でもう一つが紹介されています。
それは[]演算子の戻り値は値渡しでも、実質的には参照渡しであるということです。
例を挙げると、以下のようなコードは未定義動作です。

auto first = std::vector<bool>{false, false}[0];   //参照渡しと実質的に同義な値渡し
first = true;                                                        // 死んだオブジェクトに代入する未定義動作
  • std::vector<bool>::reference 君は、各要素を参照してるっぽく動作するため、ビットとして存在する実態にアクセスするための情報を持っています。実装方法によっては、ポインタとそこから何ビット目かといった感じの情報です。
  • 上記例では、std::vector<bool>::reference を first は受け取ります。そのため first は std::vector<bool>{false, false} と宣言された右辺値の内部を指し示すポインタを持つことになります。
  • 右辺値は一行だけの命です。つぎのセミコロンが来たらそこで死にます。
  • つまりfirst = true と代入している時点で、first 内部のポインタは、すでに死んだvectorが存在していた位置を指し示します。このコードは代入によってメモリ空間をぐちゃぐちゃにできるわけです。
  • このようなバグは、関数の戻り値(値渡し)でももちろん起こりえます。
  • std::vector<bool>怖い…

 

std::vector<bool>::referenceのような、ほかの型の模倣や拡張を目的としたクラスを、プロクシクラス(proxy class)とか言うらしいです。

このようなクラスの存在に気づくためには、ヘッダファイルの実装を自分で見ようねという話みたいです。C++楽じいなぁ…

 

C++は楽しい。

 

 

< 前の項目へ   次の項目へ >  // そのうち…

C++が使えたい 項目5(auto – 1)

サバです。
前回の記事は投稿後に大分変更・加筆をしたので、チラ見してくださいお願いします。
それに加えて、詰め込みすぎたので今回から1項目くらいずつで行きたいと思います。

 

<以下長い注意>
ここの内容は、サバが適当な自己解釈を重ねた結果、攪拌され白濁してしまった知識の泉から、沈殿物を素手で持ち上げようとした結果です。保証はしかねます。ご了承ください。

内容はEffectiveModernC++をわざわざ書き写したような何かです。
題名にEffectiveModernC++が入っていないのは更新が途中で止まる自信があるからです。

また、もしよろしければ、間違い、気になったところ、分かりづらいところなどを指摘してもらえると、サバがピチピチ跳ねて喜びます。喜びすぎてプログラミング言語C++第四版をあなたに投げつけるかもしれません。
(ぜひ教えてください。よろしくお願いします。 < ( _ _ ) > 何でもするとは言いませんから…

 

 

 

項目5 明示的型宣言よりも auto を優先する

 

結論:いいものはどんどん使おう!

IDEあるならとりま使いましょう。ぐう有能。
(もちろん注意するとこもあるみたいですが…

具体的な使用例(特に変数宣言範囲for文!!)

変数宣言 auto value = 0;  int
範囲for文 for (const auto& content : container) { } 範囲for文 + auto&は最強
関数の戻り値 auto func(){ return “auau”; } const char *
ラムダ式の引数 auto lambda = [](auto& arg)

    std::cout << arg;
};
これはC++14以降の機能

 

 

 

 

・auto ? なにそれおいしいの?

    つ autoはおいちい。(前回の記事)
   もし良ければ下にある「autoの利点」もどうぞ…
     ( このサイトが大好き。 : cpprefjp) // 神様です。いつもありがとうございます。
  

 

 

 

 

・深淵を覗きに行くために…

 auto の利点

 ・変数の初期化忘れがなくなる

    auto i = 0;
  • 型推論のために必ず初期値を指定する必要がある。

 

 ・コンパイラが適切な型を選んでくれる

    std::vector<int> vec;
    auto size = vec.size(); 
  • std::vector<int>のサイズを表す非負整数型は std::vector<int>::size_type だけど、そんな長いの覚えてコーディングする必要がなくなる。

 

 ・労力が減る

    std::vector<int> data(10)

    for (auto& val : data)
        std::cin >> val;

    for (auto ite = data.crbegin(); ite != data.crend(); ++ite)
        std::cout << *ite << std::endl;

  • 入力から10個の整数を受け取って逆順で出力するプログラム
  • std::vector<int> の int を書き換えるだけでどんな型にも対応できる(double, std::string, long, etc…)
  • イテレータの型指定で悩むこともなくなる
  • コンパイラに任せた方がヒューマンエラーは減る。楽しよう!

 

 

私は脳死して使うようにしています。autoだいしゅき。
C++に追加されたということは、いいからだまって使えという機能のはずですから。(盲信)

 

 

< 前の項目へ   次の項目へ >

C++が使えたい 項目1~4(型推論)

サバです。最近EffectiveModernC++をやっと買えたと思ったら、ついでに期末試験が迫ってきてしまったので、現実逃避しながらその内容について書いてきたいと思います。(本の内容を自分解釈で出力しとけば忘れたときに楽かなぁ…とも思ったので!)
この本にとっかかりやすくなってもらえるとうれしいなぁ

 

<注意>
ここの内容は、サバが適当な自己解釈を重ねた結果、攪拌され白濁してしまった知識の泉から、沈殿物を素手で持ち上げようとした結果です。保証はしかねます。ご了承ください。

題名にEffectiveModernC++が入っていないのは更新が途中で止まる自信があるからです。

また、もしよろしければ、間違い、気になったところ、分かりづらいところなどを指摘してもらえると、サバがピチピチ跳ねて喜びます。喜びすぎてプログラミング言語C++第四版をあなたに投げつけるかもしれません。
ぜひ教えてください。よろしくお願いします。 < ( _ _ ) > 何でもするとは言いませんから…

 

 

 

 

1章 型推論

 

項目1 テンプレートの型推論を理解する

 

結論:大体期待通り!

C++の深淵は闇の中だとしても、templateは晴れた日のあなたの部屋くらい明るい。
C++なんて、第一に安全、第二に効率を考えて、Cから派生したってことだけ分かっていれば、もう何も怖くない!!(そう願いたい…)
まさにtemplateはそんな感じ。(希望的観測)

 

template<typename T>  と宣言したとき、Tの使い方は参照について考えると3つあります。

1. void f (T arg) { }      : arg は値渡し

  • const , volatileは完全になかったことにされる

2. void f (T& arg) { }   : arg は参照渡し

  • この関数使うときに右辺値の引数をぶち込むとエラー

3. void f (T&& arg) { } : arg はユニバーサル参照渡し

  • ユニバーサル参照 : 左辺値参照・右辺値参照のどちらもイける両刀使い

 

上記の f を f (value) と呼び出したとき、 Targ  の
推論される型を表に入れるとこうなります。大体…期待通りです。(volatileもconstと同じ)

T + & T T& T&&
T arg T arg T arg
int value; int int int int& int& int&
int& value; int int int int& int& int&
int&& value; int int int int& int& int&
const int value; int int const int const int& const int& const int&
const int& value; int int const int const int& const int& const int&
const int&& value; int int const int const int& const int& const int&
f ( 1 )   // 何か右辺値 int int error error int int&&
  • Tの時、constも参照も取れるのは、値渡しでできた複製(仮引数)がどうなろうとコピー元(実引数)には関係ないからです。
  • エラーが出るのは、右辺値を書き換えても意味がなく、リテラルの参照が存在したらわけわかめになるからだとおもいます。(適当)
  • ユニバーサル参照はいろいろ変態なので覚えるのが大変そうです…

 

 

・template? 知らない子ですね

  君は、関数にを渡すだけの退屈な日々を過ごす中で、
  型も渡せたらいいのにと思ったことはあるかい?
  そういうことだよ。()

 

 

・深淵を覗きに行くために…
 (闇のコードを読むことに興味がある方は知っているべき?)

ついでにポインタ、配列、関数も入れてみましょう

T + & + α T T& T&&
T arg T arg T arg
const int * const value; const int * const int * const int * const const int * const & const int * const const int * const &
int value[4]; int * int * int [4] int (&)[4] int [4] int (&)[4]
void value( int ); void (*)(int) void (*)(int) void (int) void (&)(int) void (int) void (&)(int)
  • int value[4] は長さ4の int 型配列、void value( int ) は int 型引数一つを受け取る void 関数です。
  • 注目するは青文字です。値渡しでは配列や関数をポインタとして推論しています。これはC言語由来の動作で、配列や関数がポインタに成り下がる(本当はポインタじゃないけどポインタのふりをする)という動作をするためです。
  • 参照渡しはポインタのふりをする必要がないため、配列・ポインタの参照型として推論されます。

 

Tにconstつけるとこうなります。
 (VS2017でこうなるを確認できたけど理由がよくわかってなかったり…

T + const + & const T const T& const T&&
T arg T arg T arg
int value; int const int int const int& error error
int& value; int const int int const int& error error
int&& value; int const int int const int& error error
const int value; int const int int const int& error error
const int& value; int const int int const int& error error
const int&& value; int const int int const int& error error
f ( 1 )   // 何か右辺値 int const int int const int& int const int &&
  • const T&& は要らない子だと思ってたけど右辺値だけ受け取りたいときに使える…?
  • 右辺値と右辺値参照が頭の中でこんがらがってあぁぁぁぁ 
  • 迷ったら const 参照使ってれば一番よさそうですね

 

ポインタになるとこうなります
  ※ const が長くて表にはまらなかったのでcとしました

T + const + * T * T * const const T * const T * const
T arg T arg T arg T arg
int * value;    int    int*    int    int* c  int  c int*  int  c int* c
int * const value;    int    int*    int    int* c  int  c int*  int  c int* c
const int * value; c int int* c int c int* c  int  c int*  int  c int* c
const int * cosnt  value; c int c int* c int c int* c  int  c int*  int  c int* c
  • const とポインタの関係が完璧に分かっているconst教信者の方なら、const T * とか T * const とかしたときはどうなるか、すぐに予想できましたよね?(震え声)

 

ね? templateって、素直ないい子…だよ…ね?

 

 

 

 

 

 

 

項目2 autoの型推論を理解する

 

結論templateと同じ!以上!解散!


とは言えない
みたいです…
1か所だけ異なって、auto value = {1, 2, 3, 4, 5, 12}; と{}で書いたときのみ、
value は std::initializer_list<T>という型に推論されます。

左右は全く同じ意味
auto i = 1; int i = 1;
const auto ci = i; const int ci = i;
const auto d = 1.0; const double d = 1.0;
auto d2 = d; double d2 = d;
auto& ir = i; int& ir = i;
const auto& cir = ir const int& cir = ir;
auto& cir2 = cir; const int& cir2 = cir;
auto hentai = {1}; std::initializer_list<int> hentai = {1};
auto hentai = {1, 1.0}; error (intなのかdoubleなのか、少しでも曖昧なため)

 

 

・auto? 知らない子ですね 

  C++にはイテレータというtemplateクラス版ポインタがあるんだけど、
  そいつが指す変数の「」を取得するためにこんなコードは書きたくないよね?

  template<typename Iterator>
  void PrintValue(Iterator iterator)
  {
      std::iterator_traits<iterator>::value_type value = *iterator;
      std::cout << value;
  }

  大丈夫。こんなの覚えなくていい。そう、autoならね。

  template<typename Iterator>
  void PrintValue(Iterator iterator)
  {
      auto value = *iterator;
      std::cout << value;
  }

 

 

・深淵を覗きに行くために…

ラムダ式の引数や関数の戻り値など、関数の定義等にもautoを使えますが、
これはtemplateの型推論になるらしいです。

    auto lambda = [](auto arg){ };
    lambda({1, 3, 4});   // error std::initializer_list<int>と推論できない

    auto plsGiveMe_initializer_list()
    {
        return {1, 2, 3};    // error std::initializer_list<int>と推論できない
    }

また、std::initialzier_list<>の型推論には以下のようにC++のバージョンに左右される面があります。(デフォルトのVisualStudio2017は、C++17以降の方の動作をします。)

ソースコード 推論結果
C++14以前 C++17以降
auto value = {0, 1, 2}; std::initializer_list<int> value = {0, 1, 2}; std::initializer_list<int> value= {0, 1, 2};
auto value = {0}; std::initializer_list<int> value = {0}; std::initializer_list<int> value = {0};
auto value{0, 1, 2}; std::initializer_list<int> value = {0, 1, 2}; error
auto value{0}; std::initializer_list<int> value = {0}; int value = 0;

 

 

 

 

 

 

 

 

項目3 decltypeを理解する

 

結論 : そのまんま型が返ってくる!(大体そのまんま)

 

decltypeにも例外的な動作が一つあります。
単純に名前ではない左辺値式をあたえたときは
参照がもれなく付いてくるというものです。

int n;                         // 左辺値式 n
decltype(n) → int      // 単純に名前だけの左辺値式
decltype((n)) → int& // この場合は()が付いたため複雑な左辺値式と判定されるらしい…

 

項目2の変数と次の関数を使います。   void kansu(int, double){}  

左右は全く同じ意味
decltype(i) int
decltype(cir2) const int&
decltype(hentai) std::initializer_list<int>
decltype(kansu(0, 0)) void
decltype(kansu) void ( int, double )
decltype((i)) int&      // 唯一の例外
decltype((i + 1 + 0.1)) double  // 右辺値式は例外に含まれない

 

 

・decltype? 知らない子ですね

知らなくても大丈夫。ゴリゴリtemplate書くようになるまで必要になることはないから。
それに、必要になったらいやでも覚えなきゃいけないから。(未来の自分がそう言ってる気がする)

 

・深淵を覗きに行くために…

decltypeは、autoの用に変数の型・関数の戻り値を推論するのに使えます。書式は以下の通りです。
ただし推論の方法はdecltypeのものであり、auto や template とは異なります。

左右は全く同じ意味
decltype(auto) value = 0;
decltype(auto) func()
{
    return 0;
}
int value = 0;          // 0 はint型
int func()                 // 0 はint型
{
    return 0;   
}
const int n = 0;
decltype(auto) value = n;
decltype(auto) func()
{
    return n;
}
cont int n = 0;
const int value = n;  //templateやautoはintと推論
const int func()        //templateやautoはintと推論
{
    return n;
}
int n = 0;
decltype(auto)
value = (n); // 複雑な左辺値式
decltype(auto) func()
{
    return (n);     // 複雑な左辺値式
}
int n = 0;
int&
value = n;          // 参照が付きます
int& ILoveDecltype() // 参照が付きます
{
    return n;
}
decltype(auto) value = {1, 2, 3};
decltype(auto) func()
{
    return {1, 2, 3}; // std::initializer_list<int> ?
}
error
(autoを使いますが、型の推論方法はdecltypeのものです。そのためstd::initializer_list<int>とは推論できません。)

 

 

 

 

 

 

 

項目4 推論された型を確認する

 

結論 : IDEに頼ろう!

IDE(VisualStudioとかとかとか)で気になるトークンにマウスオーバー。
これが一番(個人比)
Boost.TypeIndexというBoostライブラリも紹介されていました。
でも変な型が出る可能性は十分あるという…

そのほかにはコンパイルエラーで確認する方法、場合によっては見づらい・不正確な型名を表示する可能性がある方法なども紹介されていました。

 

 

 

 

 

 

 

< 前の項目へ   次の項目へ >

ICPC2018国内予選参加記 Tech-ONC

こんばんえるえる〜〜
毎度ガナリヤです!
部室行かなくてすいません!活動はしてるんです!集中力の問題で家でやってます・・・(お兄さん許して・・・)
今日は、ICPC国内予選に参加したので、来年のリベンジのために参加記を残しておこうとおもいます。
 
 

チーム Tech-ONC

チームTech-ONCはTNP部員かつ、同じ競技プログラミングバイト先の三人で構成されています。
思考力・実装力担当の我らが部長!こしょう!
C++の魔法使い!サバ!
手広無力おじさん!ガナリヤ!
の三人です。
このチームの特徴として

  • 普段アルバイトで、競技プログラミング問題制作を行っている
  • 得意なことがそれぞれ違い、バランスが取れている
  • 三人共コーディングを担当できる
  • いい意味でも悪い意味でも、そこまで競技プログラミングコーディング力が離れていない(他チームは、一人だけ無茶苦茶強いところが多かったりする)

という点があります。
僕が誘わせていただき、引き受けていただきて本当に嬉しかったです!


参加記

事前準備

統計の授業を三人で抜け出してご飯を食べる。
チョコレートがおいしかった。
その後、コーディング場に移動し、それぞれマクロや本の準備をしていた。
その間、サバがVisualStudio設定RTAしてて面白かったです、来年は僕も設定できるようになりたい

16:10頃、監督の先生が到着(本当に引き受けていただきありがとうございます!)
雑談をしつつ、そわそわしながら開始を待つ。
  

ICPC予選開始

16:30から予選開始。
サバニキが印刷をぶん回しつつ、僕がA問題の紙を読み、こしょうがB問題の紙を読み始めました。
その間、サバがVisualStudioの設定やマクロの準備をしており、縁の下の力持ちでした!(またnext_combinationの実装もしてもらってしまった・・・来年までにはもっと簡単な実装にします・・・)サバありがとう!

A問題 所得格差は、N人の合計の平均を取って、平均より小さい人数を数えるだけの問題で非常に簡単でやるだけでした。年々A問題は簡単になっている気がする・・・
ハラハラしつつ、doubleのガバに気をつけてAC

  

B問題とC問題

A問題を解いた後、こしょうがB問題へ。
僕とサバでC問題に移動。

こしょうにきが解いている間、C問題を二人で話し合う。
C問題 みなとハルカス

C問題は、整数bが与えられ、1~Nまでの中で連続した整数の和がbに一致する中で、最長のものを出力せよという問題でした。
二人で話し合った結果、部分累積和で10^8までぶん回すか、しゃくとり法で実装するかの二択になりました。
ここで壮大なミス。
絶対に部分累積和じゃ通らないのに、部分累積和の案を採用。ふたりともしゃくとり法はあまりお得意なお気持ちではないため、部分累積和に逃げてしまった・・・、このミスが無ければDの実装が間に合ったかもしれない(申し訳ない)

間違った部分累積和の実装が行けそうになったとき、こしょうの実装でエラーが発生していた模様。
コードを印刷してもらって、エディタを空けてもらって入れ替わりで僕が嘘解法を実装。
ちなみに、今回のB問題はCよりずっと難しかったようで、さらっと解いてるこしょうやっぱやばい。

嘘解法のCを実装した僕
「できた!天才か〜〜〜?」←嘘解法で喜んでいるクズの図
10^9の実装のため、テストケースだけ通して、もう一度こしょうと交代。
こしょうがB問題の添字ミスと、問題文の勘違いに気づいたらしく難なくAC。こしょうにきは、問題文読み間違い以外基本的にすべて通している(すごい(すごい))

その後、こしょうに変わってもらって、投げていたテストケースを見ると、数が合っておらず、嘘解法であったことに気づく(当たり前だよなぁ?)
急いで、しゃくとり法で実装してAC。やっぱ最初からしゃくとり法にするべきでした、チキっちゃだめやね・・・
  

D問題

ここから毎回僕たちが苦戦しているD問題以降パートへ。

サバニキがD問題とE問題に目をつけていて、問題概要や、解き方のコツを教えてもらった。
D問題 全チームによるプレーオフは、サッカーの試合をするとき、すべてのチームの勝敗が一致し、全員で仲良しになる総数は何個あるか?という問題だった。

僕とサバでDはやべえな・・・となり、他の問題をいろいろ見ていた。よくよく考えるとこれが悪手だった気がする。最初からずっとDのみに取り組んでいれば、時間が間に合ったかもしれない(悔しい・・・)

その後、こしょうもDに参戦し、こしょうとサバが解き方の方針を教えてくれた。(5人なら、必ず全てのチームが2勝2敗とか、普段の自分一人のコーディングなら絶対に気づけてない・・・)

その後しばらく三人でDにうなり続け、うだうだ言いながら、意見を出しつつ、next_combinationで1と0の組み合わせを求めれば計算量はあまり増えないということに気づく(正直合ってるか分からん)
よくわからないまま、時間が無いため、僕が突発的に考えた多分正攻法ではない、汚いアイディアを実装することに。
僕とこしょうでペアプロしつつ、サバがnext_combinationの実装をしてくれた(来年までにはもっと分かりやすいライブラリつくる許して・・・)
こしょうとサバのこしょうを借りつつ、ガバガバ実装を行い、時間と戦いつつ、コーディングが完成。
奇跡に祈りつつ、実行したところ、添字エラー。添字エラーを解消している中で、時間になってしまった・・・

  


振り返り

ICPCに参戦してみて、すんごい楽しかったです。
他の人と、問題を解くためのアイディア出し合ったり、ふざけながら問題通すのって、学生のうちしかできない気がする(仕事になると、ふざけることはできない・・・)
また、自分の実装の弱さに気付かされた回でもありました・・・。他の二人の実装がすごい綺麗で、僕のコーディングがかなり汚いので、デバッグしづらくて二人がかなり僕のコードを追うの辛かったと思います。もっと簡潔なコーディングできるよう目指していきたいです。

何はともあれ、すごい楽しかった
来年もこの三人でTech-ONCとして出るという話をした。
来年は三人共、競技プログラミングがうまくなり、僕はAtCoder年内中に青色、来年のICPCまでに黄色になっているはずなので(希望的観測)、来年のチーム名はTech-ONS(O(N^2))でしょうか・・・

何も考えず、今日あったことをそのまま書いているだけで、読みづらいと思います。すいません。
それぐらい楽しかったです
明日はSoundHound頑張ろうと思います!

ガナリヤでした!

Top