えびちゃんの日記

えびちゃん(競プロ)の日記です。

vector の多次元版をつくるやつ

自分が楽に作れるようになると忘れがちなんですが、どうやらこれを書くのに疲弊する人がまだいるようです。

size_t n1, n2, n3;
// ↑入力を受け取るとかして、値が入るとする
vector<vector<vector<int>>> v(n1,
  vector<vector<int>>(n2,
    vector<int>(n3, x)));

みたいに書くのはさすがに大変だと思います。 タイピングに自信があっても、競技中にこんなのを書く時間があるなら考察をしたいでしょう。

C++17 なら以下のように書けますが、だとしてもまだ長いです。

vector v(n1, vector(n2, vector(n3, x)));

あれ、長いですか? 別にいい気がしますね...(おわり)

書く予定だったこと

auto v = make_vector({n1, n2, n3}, x);
auto v = make_vector<int>({n1, n2, n3});  // 型だけ指定したいとき

auto v = make_vector(n1, n2, n3, x);
auto v = make_vector<int>(n1, n2, n3);  // 型だけ

のように書けるとうれしい気がしたんですよね。

前者は えびちゃんのC++17 未満でも動く版)、後者は すいばかさん・びーとくんの です。 えびちゃん的には、サイズに関する引数が見やすいので前者の方がすきです。 後者は、(値指定と型だけの)二つを両立させられない気がしますが、前者はできます。

(えびちゃんのをコピペしようとする人へ:size_t を常用する人でなければ、関数の中の size_tint に直して使うといいと思います)

運用でカバーすればどうにでもなるので好きな方を使えばいいと思います(タイプ数とかは後者のが短いし)。 型だけ指定したいというのは、初期値は入力から受け取りたいとかを想定していますが、以下のどちらかで対応できるので。

  • 所望の型の適当な値を入れておく(0LL とか 0.0 とか)
  • 入力をするパートまで行う用の関数は別で作っちゃう
    • make_vector_from_input<double>(n1, n2, n3) みたいなこと

生配列使えば?

固定長で大きめのを作る実装だと、「最大ケースでは配列外参照するがサンプルでは気づかない」というのがよくあるので、きらいです。 それでもしたい人はすればいいと思います。

ところで

Rust だと、自前で何かを定義しなくても

let mut v = vec![vec![vec![x; n3]; n2]; n1];

と書けます*1

macro_rules! mulvec {
    ($x:expr; $s:expr) => {
        vec![$x; $s]
    };
    ($x:expr; $s0:expr; $( $s:expr );+) => {
        mulvec![vec![$x; $s0]; $( $s );+ ]
    };
}

とか書けば、

let mut v = mulvec![x; n3; n2; n1];

とかも書けます。Rust のマクロは C++ よりも強力なのでいろいろと便利です。 C++ でよくあるマクロの罠にも苦しめられない上、構文もある程度自由です。 「この文字を区切り文字にして任意回繰り返してねー」というのを指定したり、構文を複数用意しておいて「こっちの構文で呼び出されたらこう展開して、こっちならこうしてねー」みたいなこともできます。

デバッグ出力もいくつか用意されていて、自前で何かを書かなくてもいいです。

eprintln!("{:?}", v);
eprintln!("{:#?}", v);  // 出力形式がちょっと変わる
dbg!(x);  // 行数とかも出力してくれる

ねこちゃん

おわりです。

C++固執したがる競プロ er が Rust を書きたくなりそうな機能の大喜利みたいなのがあるとたのしそう。 人によって違いそうだけど。

*1:これ実は C++17 で書けるやつとあんまり変わらないですね。