前回 の続きです。

boost::indices は、boost::multi_array の部分ビューを作るためのオブジェクトです。

部分ビューでは、元の配列の一部を仮想的な配列とみなしてアクセスすることができます。 また、ストライドを指定して2刻みでアクセスしたり、一部の次元を縮退させてビューの次元数を減らすこともできます。

#include <boost/multi_array.hpp>

int main()
{
    //  3 次元配列を作成
    using boost::detail::multi_array::multi_array_view;
    using boost::multi_array_types::index_range;
    boost::multi_array<float, 3> arr(boost::extents[3][4][5]);
    for (size_t i = 0; i < 3; ++i)
    {
        for (size_t j = 0; j < 4; ++j)
        {
            for (size_t k = 0; k < 5; ++k)
            {
                arr[i][j][k] = i * 100 + j * 10 + k;
            }
        }
    }

    //  以下の範囲で部分ビューを作成
    //  i: 1
    //  j: [0, 1, 2, 3]
    //  k: [0, 1, 2, 3, 4]
    multi_array_view<float, 2> sub =
        arr[boost::indices[1][index_range(0, 4)][index_range(0, 5)]];
    for (size_t j = 0; j < 4; ++j)
    {
        for (size_t k = 0; k < 5; ++k)
        {
            //  最初の次元は縮退しているため、2次元配列となる
            printf("%.1f\t", sub[j][k]);
        }
        printf("\n");
    }
    return 0;
}
$ # macOS Sierra (10.12) + Homebrew
$ clang sample.cpp -o sample -I /usr/local/include/ -lc++
$ ./sample
100.0	101.0	102.0	103.0	104.0
110.0	111.0	112.0	113.0	114.0
120.0	121.0	122.0	123.0	124.0
130.0	131.0	132.0	133.0	134.0

部分ビューを作るには、boost::indices に続けてすべての次元の範囲を指定します。

定数を指定すると、次元は縮退し、ビューから取り除かれます。また、index_range のデフォルトコンストラクタを使うと元の配列の範囲を維持できます。

//  i = 1, j = 2, k = [0, 1, 2, 3, 4]
multi_array_view<float, 1> sub1 =
    arr[boost::indices[1][2][index_range()]];

//  i = 1, j = [2], k = [0, 1, 2, 3, 4]
//  sub1 と同じ領域ですが、第2次元が縮退していないため2次元配列 (1x5) となります。
multi_array_view<float, 2> sub2 =
    arr[boost::indices[1][index_range(2, 3)][index_range()]];

//  元の配列と同じ大きさの部分ビューを作成
multi_array_view<float, 3> sub3 =
    arr[boost::indices[index_range()][index_range()][index_range()]];

各次元の範囲は、メソッドチェーンや不等式で表現することもできます。

#include <boost/multi_array.hpp>

int main()
{
    //  上と同じ方法で配列を作成

    //  i: 1
    //  j: [1, 2]
    //  k: [0, 2]
    index_range r2 = (ptrdiff_t(1) <= index_range() < ptrdiff_t(3));
    index_range r3 = index_range().start(0).finish(4).stride(2);
    multi_array_view<float, 2> sub = arr[boost::indices[2][r2][r3]];
    for (size_t j = 0; j < sub.shape()[0]; ++j)
    {
        for (size_t k = 0; k < sub.shape()[1]; ++k)
        {
            printf("%.1f\t", sub[j][k]);
        }
        printf("\n");
    }
    return 0;
}
$ # macOS Sierra (10.12) + Homebrew
$ clang sample.cpp -o sample -I /usr/local/include/ -lc++
$ ./sample
210.0	212.0
220.0	222.0

boost::indices の実装

boost::indices は、<boost/multi_array/base.hpp>boost::detail::multi_array::index_gen<0, 0> 型のグローバルオブジェクトとして定義されています。

//  説明に必要な部分のみ抜粋
namespace boost {
    namespace multi_array_types {
        typedef detail::multi_array::index_gen<0, 0> index_gen;
    }
    multi_array_types::index_gen indices;
}

index_gen の実装は以下のようになっています。

//  説明に必要な部分のみ抜粋
namespace boost::detail::multi_array {

//
//  NumRanges は入力配列の次元数、NumDims はビューの次元数です。
//  したがって、次元の縮退がある場合は NumRanges > NumDims となります。
//
template <int NumRanges, int NumDims>
struct index_gen {
private:
    typedef boost::detail::multi_array::index index;
    typedef boost::detail::multi_array::size_type size_type;
    typedef index_range<index, size_type> range;

public:
    typedef typename range_list_generator<range,NumRanges>::type range_list;

    //  N 次元のインデックス全てを保持する public フィールド。
    //  実体は boost::array<index_range, size_t> 。
    //
    //  range_list_generator::type はテンプレートの特殊化により NumRanges = 0 の場合に
    //  boost::array<extent_range, 1> を返すようになっている。
    range_list ranges_;

    index_gen() { }

    //  index_gen<N, M> から index_gen<N + 1, M + 1> を返す演算子。
    //  boost::indices[index_range()] ->
    //      boost::indices[index_range()][index_range()] に相当。
    index_gen<NumRanges + 1, NumDims + 1>
    operator[](const range& r) const
    {
        index_gen<NumRanges + 1, NumDims + 1> tmp;
        std::copy(ranges_.begin(), ranges_.end(), tmp.ranges_.begin());
        *tmp.ranges_.rbegin() = r;
        return tmp;
    }

    //  index_gen<N, M> から index_gen<N + 1, M> を返す演算子。
    //  boost::indices[index_range()] ->
    //      boost::indices[index_range()][10] に相当。
    //
    //  この演算子は追加する次元を縮退させます。
    index_gen<NumRanges + 1, NumDims>
    operator[](index idx) const
    {
        index_gen<NumRanges + 1, NumDims> tmp;
        std::copy(ranges_.begin(), ranges_.end(), tmp.ranges_.begin());
        *tmp.ranges_.rbegin() = range(idx);
        return tmp;
    }
};

}

index_genindex_range の配列を持っており、各次元のスライスを表現します。 このオブジェクトのテンプレート型引数は index_range<ptrdiff_t, size_t> です。

index_range の定義は以下のとおりです。 プロパティとして、start, finish, stride および、縮退した次元をあらわす is_degenerate があります。

//  説明に必要な部分のみ抜粋
namespace boost::detail::multi_array {

template <typename Index, typename SizeType>
class index_range {
public:
    typedef Index index;
    typedef SizeType size_type;

public:
    //  次元上のすべての要素。
    index_range();

    //  index == pos となる要素。
    explicit index_range(index pos);

    //  start <= index < finish となる要素。
    //  ストライドを指定することができます。
    explicit index_range(index start, index finish, index stride = 1);

    //  メソッドチェーンによる表記。
    index_range& start(index s);
    index_range& finish(index f);
    index_range& stride(index s);

    index start() const { return start_; }
    index finish() const { return finish_; }
    index stride() const { return stride_; }
    bool is_degenerate() const { return degenerate_; }

    //  その他、いくつかの補助関数があります。
    //  詳細はソースコードを参照してください。

public:
    index start_, finish_, stride_;
    bool degenerate_;
};

//  以下は比較演算子の実装です。
template <typename Index, typename SizeType>
inline index_range<Index, SizeType>
operator<=(Index s, const index_range<Index, SizeType>& r);

template <typename Index, typename SizeType>
inline index_range<Index, SizeType>
operator<(Index s, const index_range<Index, SizeType>& r);

template <typename Index, typename SizeType>
inline index_range<Index, SizeType>
operator<(const index_range<Index, SizeType>& r, Index f);

template <typename Index, typename SizeType>
inline index_range<Index, SizeType>
operator<=(const index_range<Index, SizeType>& r, Index f);

}

ソースコードのライセンス

// Copyright 2002 The Trustees of Indiana University.

// Use, modification and distribution is subject to the Boost Software
// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

//  Boost.MultiArray Library
//  Authors: Ronald Garcia
//           Jeremy Siek
//           Andrew Lumsdaine
//  See http://www.boost.org/libs/multi_array for documentation.

参考資料