コンパイル時ズンドコキヨシ
中3女子です。
巷でズンドコキヨシなるものが流行っていたようなので C++11 constexpr でやってみた。
Javaの講義、試験が「自作関数を作り記述しなさい」って問題だったから
— てくも (@kumiromilk) 2016年3月9日
「ズン」「ドコ」のいずれかをランダムで出力し続けて「ズン」「ズン」「ズン」「ズン」「ドコ」の配列が出たら「キ・ヨ・シ!」って出力した後終了って関数作ったら満点で単位貰ってた
コード:
#include <cstddef> #include <type_traits> #include <stdexcept> #include <sprout/utility/any_convertible.hpp> #include <sprout/type_traits/enabler_if.hpp> #include <sprout/array.hpp> #include <sprout/string.hpp> #include <sprout/random/default_random_engine.hpp> #include <sprout/random/bernoulli_distribution.hpp> #include <sprout/random/unique_seed.hpp> #include <sprout/generator/functions.hpp> #include <sprout/range/adaptor/reversed.hpp> #include <sprout/algorithm/string/join.hpp> static constexpr std::size_t default_limit = 96; static constexpr std::size_t len = std::extent<std::remove_reference<decltype("キ・ヨ・シ!")>::type>::value - 1; typedef sprout::string<len> string_type; template<std::size_t N> struct zundoko_result : public sprout::algorithm::results::join<sprout::array<string_type, N> > {}; template<std::size_t N> using zundoko_result_t = typename zundoko_result<N>::type; inline constexpr string_type zundoko(bool c) { return !c ? sprout::to_string("ズン") : sprout::to_string("ドコ") ; } template< std::size_t N, typename Rnd, typename... Args, typename sprout::enabler_if<!(sizeof...(Args) + 6 <= N)>::type = sprout::enabler > inline constexpr typename zundoko_result<N>::type kiyoshi(Rnd const&, bool, bool, bool, bool, Args...) { return throw std::runtime_error("exceeded"), sprout::any_convertible(); } template< std::size_t N, typename Rnd, typename... Args, typename sprout::enabler_if<(sizeof...(Args) + 6 <= N)>::type = sprout::enabler > inline constexpr typename zundoko_result<N>::type kiyoshi(Rnd const& rnd, bool e0, bool e1, bool e2, bool e3, Args... args) { return !sprout::generated_value(rnd) || e0 || e1 || e2 || e3 ? kiyoshi<N>(sprout::next_generator(rnd)(), sprout::generated_value(rnd), e0, e1, e2, e3, args...) : sprout::algorithm::join( sprout::array<string_type, N>{{ sprout::to_string("キ・ヨ・シ!"), zundoko(sprout::generated_value(rnd)), zundoko(e0), zundoko(e1), zundoko(e2), zundoko(e3), zundoko(args)... }} | sprout::adaptors::reversed ) ; } template< std::size_t N, typename Rnd, typename... Args, typename sprout::enabler_if<(sizeof...(Args) + 1 <= 4)>::type = sprout::enabler > inline constexpr typename zundoko_result<N>::type kiyoshi(Rnd const& rnd, Args... args) { return kiyoshi<N>(sprout::next_generator(rnd)(), sprout::generated_value(rnd), args...); } template< std::size_t N = default_limit, typename URNG, typename sprout::enabler_if<!std::is_integral<URNG>::value>::type = sprout::enabler > inline constexpr typename zundoko_result<N>::type zundoko_kiyoshi(URNG const& urng) { return kiyoshi<N>(sprout::bernoulli_distribution<>()(urng)); } template<std::size_t N = default_limit> inline constexpr typename zundoko_result<N>::type zundoko_kiyoshi(std::size_t seed) { return zundoko_kiyoshi<N>(sprout::default_random_engine(seed)); } template<std::size_t N = default_limit> inline constexpr typename zundoko_result<N>::type zundoko_kiyoshi() { return zundoko_kiyoshi<N>(SPROUT_UNIQUE_SEED); } #include <iostream> int main() { constexpr auto result = zundoko_kiyoshi(); std::cout << result; }
出力:
ズンドコズンズンドコズンズンドコドコズンズンドコドコズンドコズンズンドコドコズンドコドコズンドコドコドコドコズンドコズンドコズンズンズンズンズンドコキ・ヨ・シ!
犬小屋でコンパイル&実行:
[Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ
処理の流れ
- コンパイル時乱数生成エンジンを用意する
- ベルヌーイ分布で false/true(ズン/ドコ)の乱数を発生させ、可変引数テンプレートのパラメータパックに追加していく
- パラメータパックは逆順になる(生成順が {ズン, ズン, ズン, ズン, ドコ} なら Args = {ドコ, ズン, ズン, ズン, ズン} になる)
- → なぜなら、パラメータパックは末尾より先頭から取り出すほうが効率的だから
- {ズン, ズン, ズン, ズン, ドコ} 列になったら各要素を文字列に変換して "キ・ヨ・シ!" を追加した配列を作る
- reversed アダプタで配列を反転する
- join で結合した文字列を返す
- ――――ここまでコンパイル時――――
- (実行時)文字列を出力する
これら処理を書くために Sprout ライブラリのうち以下のコンポーネントを使った。
- constexpr Arrayクラス
- constexpr 文字列クラス
- constexpr 乱数
- constexpr Rangeアダプタ
- constexpr 文字列アルゴリズム