transform_if?

と、いうものを Twitter で見かけたのでちょっと書いてみました。
transform_if の挙動としては、

  1. cond == true の要素のみを関数に適用させる(range のサイズはそのまま)
  2. 要素をフィルタリングしてから関数に適用させる(filter + transform)

と、いう処理が考えられますが、ちょっと違うアプローチを考えて見ました。

[ソース]

#define BOOST_RESULT_OF_USE_DECLTYPE

#include <iostream>
#include <pstade/oven/transformed.hpp>
#include <pstade/oven/filtered.hpp>
#include <pstade/oven/counting.hpp>
#include <pstade/oven/identities.hpp>
#include <pstade/oven/io.hpp>
#include <pstade/result_of_lambda.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/optional.hpp>

struct boost_optional_add_star{
    template<typename T>
    struct result;

    template<typename F, typename T>
    struct result<F(boost::optional<T>)>{
        typedef T type;
    };

    template<typename F, typename T>
    struct result<F(T)>{
        typedef T type;
    };

    template<typename T>
    typename result<boost_optional_add_star(boost::optional<T>)>::type
    operator ()(boost::optional<T> const& t) const{
        return *t;
    }

    template<typename T>
    typename result<boost_optional_add_star(T)>::type
    operator ()(T const& t) const{
        return t;
    }
};


template<typename Range, typename Func>
auto
maybe_transform(Range const range, Func f)
->decltype(range
    |pstade::oven::transformed(f)
    |pstade::oven::filtered(boost::lambda::_1)
    |pstade::oven::transformed(boost_optional_add_star())
){
    return range
        |pstade::oven::transformed(f)
        |pstade::oven::filtered(boost::lambda::_1)
        |pstade::oven::transformed(boost_optional_add_star());
}

int
main(){
    using pstade::oven::counting;
    using pstade::oven::identities;

    std::cout << (
        maybe_transform(counting(0, 10), [](int n){
            return n % 2 == 0 ? boost::optional<int>(n + n) : boost::none;
        })
    |identities) << std::endl;

    std::cout << (
        maybe_transform(counting(0, 10), [](int n){
            return n % 2 != 0 ? n * n : n;
        })
    |identities) << std::endl;

    // 0 は false なのでフィルタリングされる
    std::cout << (
        maybe_transform(counting(0, 10), [](int n){
            return n % 2 == 0 ? n + n : 0;
        })
    |identities) << std::endl;

    return 0;
}

[出力]

{0,4,8,12,16}
{1,2,9,4,25,6,49,8,81}
{4,8,12,16}

Boost.Optional を関数の戻り値型にすることで、関数内で要素のフィルタリングの有無を決定します。
false ならフィルタリングを行い、そうでなければ * を付けて返すような感じ。
こんなアプローチもありかな。

[コンパイラ]

  • g++ (GCC) 4.7.0 20111029 (experimental)