C++ で zip関数を書いてみた
最近、あまり C++ 触れられてないのでリハビリがてら書いてみた。
[ソース]
#include <array> #include <tuple> template<std::size_t ...Indexes> struct index_tuple{}; template< std::size_t Start, std::size_t Finish, std::size_t Step = 1, class Acc = index_tuple<>, bool Break = (Start >= Finish) > struct index_range{ typedef Acc type; }; template<std::size_t Start, std::size_t Finish, std::size_t Step, std::size_t ...Indexs> struct index_range<Start, Finish, Step, index_tuple<Indexs...>, false> : index_range<Start + Step, Finish, Step, index_tuple<Indexs..., Start>> {}; // 引数が2つのみ版 #if 0 template< typename T, typename U, std::size_t N, std::size_t ...Indexes > constexpr std::array<std::tuple<T, U>, N> zip( std::array<T, N> const& seq1, std::array<U, N> const& seq2, index_tuple<Indexes...> ){ return { std::make_tuple(seq1[Indexes], seq2[Indexes])... }; } template<std::size_t N, typename T, typename U> constexpr std::array<std::tuple<T, U>, N> zip(std::array<T, N> const& seq1, std::array<U, N> const& seq2){ return zip(seq1, seq2, typename index_range<0, N>::type{}); } template<std::size_t N, typename T, typename U> void zip(T, U){ static_assert(!N, "zip compile time error."); } // 可変長引数版 #elif 0 template<std::size_t N, typename ...Args> struct equal_array_size : std::integral_constant<bool, false>{}; template<std::size_t N, std::size_t Size, typename T> struct equal_array_size<N, std::array<T, Size>> : std::integral_constant<bool, N == Size>{}; template<std::size_t N, std::size_t Size, typename T, typename U, typename ...Args> struct equal_array_size<N, std::array<T, Size>, U, Args...> : std::integral_constant<bool, N == Size && equal_array_size<N, U, Args...>{}>{}; template<typename T, typename ...Args> struct zip_result_type : std::common_type< std::array< std::tuple< typename std::tuple_element<0, T>::type, typename std::tuple_element<0, Args>::type... >, std::tuple_size<T>{} > >{}; template<typename ...Seqs> constexpr auto make_tuple(std::size_t index, Seqs const&... seqs) ->decltype(std::make_tuple(seqs[index]...)){ return std::make_tuple(seqs[index]...); } template< typename Result, typename ...Seqs, std::size_t ...Indexes > constexpr Result zip( index_tuple<Indexes...>, Seqs const&... seqs ){ return { ::make_tuple(Indexes, seqs...)... }; } template< std::size_t N, typename ...Seqs, typename Result = typename std::enable_if< equal_array_size<N, Seqs...>{}, typename zip_result_type<Seqs...>::type >::type > constexpr Result zip(Seqs const&... seqs){ return zip<Result>(typename index_range<0, N>::type{}, seqs...); } template< std::size_t N, typename ...Seqs, typename = typename std::enable_if<!equal_array_size<N, Seqs...>{}>::type > void zip(Seqs const&... seqs){ static_assert(equal_array_size<N, Seqs...>{}, "zip compile time error."); } // 他の人のコードをみて、少し改良した板 // std::array を可変長で受け取っている #else template<typename ...Seqs> constexpr auto make_tuple(std::size_t index, Seqs const&... seqs) ->decltype(std::make_tuple(seqs[index]...)){ return std::make_tuple(seqs[index]...); } template< std::size_t N, typename ...Ts, std::size_t ...Indexes > constexpr std::array<std::tuple<Ts...>, N> zip( index_tuple<Indexes...>, std::array<Ts, N> const&... seqs ){ return { ::make_tuple(Indexes, seqs...)... }; } template<std::size_t N, typename ...Ts> constexpr std::array<std::tuple<Ts...>, N> zip(std::array<Ts, N> const&... seqs){ return zip<N>(typename index_range<0, N>::type{}, seqs...); } template<std::size_t N> void zip(...){ static_assert(!N && N != 0, "zip compile time error."); } #endif #include <iostream> #include <typeinfo> #include <vector> #include <boost/fusion/adapted/std_tuple.hpp> #include <boost/fusion/include/as_vector.hpp> #include <boost/fusion/include/io.hpp> template<typename T> void disp(T const& seq){ for(auto&& t : seq){ std::cout << boost::fusion::as_vector(t) << " "; } std::cout << std::endl; } int main(){ std::array<int, 5> a = {1, 2, 3, 4, 5}; std::array<char, 5> b = {'a', 'b', 'c', 'd', 'e'}; auto result = zip<5>(a, b); disp(result); // -> std::array<std::tuple<int, char>, 5> : (1, 'a'); (2, 'b'); ...; (5, 'e') std::array<int, 4> c = {1, 2, 3, 4}; // zip<4>(a, c); // // -> compile time error // // std::vector<int> d = {1, 2, 3, 4, 5}; // zip<5>(a, d); // // -> complie time error std::array<double, 5> e = {0.0, 0.5, 1.0, 1.5, 2.0}; auto result2 = zip<5>(a, b, e); disp(result2); // -> std::array<std::tuple<int, char, double>, 5> : (1, 'a', 0.0); (2, 'b', 0.5); ...; (5, 'e', 2.0) return 0; }
[出力]
(1 a) (2 b) (3 c) (4 d) (5 e) (1 a 0) (2 b 0.5) (3 c 1) (4 d 1.5) (5 e 2)
基本的には id:iorate さんの index_tuple を使って配列を展開している。
自力で書いたコードは std::array を可変長で受け取る書き方が思いつかなかったのでだいぶアレなコードになってしまった。
それ以外は他の人とだいたい同じなんじゃないかなーと。