ボレロ村上 - ENiyGmaA Code

中3女子です。

コンパイル時ズンドコキヨシ

中3女子です。


巷でズンドコキヨシなるものが流行っていたようなので C++11 constexpr でやってみた。


コード:

#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]三へ( へ՞ਊ ՞)へ ハッハッ

処理の流れ

  1. コンパイル時乱数生成エンジンを用意する
  2. ベルヌーイ分布で false/true(ズン/ドコ)の乱数を発生させ、可変引数テンプレートのパラメータパックに追加していく
    • パラメータパックは逆順になる(生成順が {ズン, ズン, ズン, ズン, ドコ} なら Args = {ドコ, ズン, ズン, ズン, ズン} になる)
    • → なぜなら、パラメータパックは末尾より先頭から取り出すほうが効率的だから
  3. {ズン, ズン, ズン, ズン, ドコ} 列になったら各要素を文字列に変換して "キ・ヨ・シ!" を追加した配列を作る
  4. reversed アダプタで配列を反転する
  5. join で結合した文字列を返す
  6. (実行時)文字列を出力する


これら処理を書くために Sprout ライブラリのうち以下のコンポーネントを使った。

  • constexpr Arrayクラス
  • constexpr 文字列クラス
  • constexpr 乱数
  • constexpr Rangeアダプタ
  • constexpr 文字列アルゴリズム


これは C++11 で動くように書いたが、C++14 ならば当然もっと簡単に書けるだろう。