FizzBuzz

会話をしていると、FizzBuzzの話になった。何かの拍子にFizzBuzzを書いてくださいと言われたらどうするだろうか、と思ってちょっと書いてみることにした。たまには気楽に書けるものもよい。

FizzBuzzを自然に捉えると、数字を文字列に変換していくルールに従って、入ってくる数字の列を変換し順に出力するというものだろう。

ということは、iota的なイテレータtransform_iteratorで覆って、ostream_iteratorに投げ込んでいけばいい。

iota的なことをしてくれるイテレータはないかと探したところ(作れるが演算子定義などが面倒だ)、boost::iterator::counting_iteratorがよいとわかった。

#include <boost/iterator/transform_iterator.hpp>
#include <boost/iterator/counting_iterator.hpp>
#include <algorithm>
#include <iostream>
#include <string>

int main(int argc, char** argv)
{
    if(argc != 2)
    {
        std::cerr << "usage: ./fizzbuzz <maximum>" << std::endl;
        return 1;
    }

    const auto fizzbuzz = [](const std::uint64_t i){
        using namespace std::literals::string_literals;
        if(i%3 == 0 && i%5 == 0) return "FizzBuzz"s;
        else if(i%3 == 0)        return "Fizz"s;
        else if(i%5 == 0)        return "Buzz"s;
        else                     return std::to_string(i);
    };

    std::copy(
        boost::make_transform_iterator(
            boost::make_counting_iterator(1ull),
            fizzbuzz),
        boost::make_transform_iterator(
            boost::make_counting_iterator(
                std::stoull(argv[1]) + 1ull),
            fizzbuzz),
        std::ostream_iterator<std::string>(std::cout, ", "));
    std::cout << std::endl;
    return 0;
}

こんなもんだろう。しかしまあ、1からmaxまでの数字の列を作って、それに関数を適用するだけだというのに非常に面倒なことをしないといけないのはやはり辛い。これくらい簡単になってもよいと思うのだが。

#include <algorithm>
#include <iostream>
#include <string>

int main(int argc, char** argv)
{
    if(argc != 2)
    {
        std::cerr << "usage: ./fizzbuzz <maximum>" << std::endl;
        return 1;
    }

    const auto fizzbuzz = [](const std::uint64_t i){
        if(i%3 == 0 && i%5 == 0) return "FizzBuzz"s;
        else if(i%3 == 0)        return "Fizz"s;
        else if(i%5 == 0)        return "Buzz"s;
        else                     return std::to_string(i);
    };
    const auto max = std::stoull(argv[1]);
    (1ull ... max).transform(fizzbuzz).output(std::cout, ", ");
    return 0;
}

と、思った辺りでBoost.rangeの存在を思い出した。

#include <boost/range/algorithm.hpp>
#include <boost/range/irange.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <iostream>
#include <cstdint>

int main(int argc, char** argv)
{
    if(argc != 2)
    {
        std::cerr << "usage: ./fizzbuzz <maximum>" << std::endl;
        return 1;
    }

    const auto fizzbuzz = [](const std::uint64_t i){
        using namespace std::literals::string_literals;
        if(i%3 == 0 && i%5 == 0) return "FizzBuzz"s;
        else if(i%3 == 0)        return "Fizz"s;
        else if(i%5 == 0)        return "Buzz"s;
        else                     return std::to_string(i);
    };

    boost::copy(boost::irange(1ull, std::stoull(argv[1]) + 1ull) |
                boost::adaptors::transformed(fizzbuzz),
                std::ostream_iterator<std::string>(std::cout, ", "));
    std::cout << std::endl;
    return 0;
}

やはりこうするとかなりマシだ。