valarray.hには valarrayの他にも、valarrayの補助クラスがいくつか存在します。 valarrayに対する基本的な演算がいくつか定義されていますが、これらのクラスはvalarrayに対する演算において、より高度な機能を提供するためのクラスです。 ここではその補助クラスとその使い方を紹介します。

indirect_arrayクラス

indirect_arrayクラスの機能の概要を説明すると、valarrayに対するインデックスの指定をsize_t型の値による単一の数値ではなく、size_t型で作成したvalarrayを利用することで、同時に複数のインデックスを指定できるような機能を提供するためのクラスです。

#include <iostream>
#include <valarray>

using namespace std;

template <class _T> void EnumerateValarray( valarray<_T>& arr )
{
    size_t i;
    size_t s = arr.size();

    for ( i = 0; i < s; i++ )
    {
        cout << arr[i] << " ";
    }
    cout << endl;
}

int main()
{
    valarray <int> arr(10);

    size_t i;
    size_t s = arr.size();

    for ( i = 0; i < s; i++ )
    {
        arr[i] = i;
    }

    cout << "arr: "; EnumerateValarray( arr );

    // 操作するインデックスをvalarrayで指定
    valarray <size_t> index(4);

    index[0] = 2;
    index[1] = 3;
    index[2] = 5;
    index[3] = 7;

    arr[index] = 0;

    cout << "arr: "; EnumerateValarray( arr );

    return 0;
}
実行結果
arr: 0 1 2 3 4 5 6 7 8 9
arr: 0 1 0 0 4 0 6 0 8 9
Press any key to continue

ここでは、indirect_arrayクラスを利用していますが、どこにもindirect_arrayなどという文字は見あたりません。 利用といっても、このような用法に対して内部的にindirect_arrayクラスが使用されるだけなので、特別な記述は必要ありません。

このサンプルのように、valarrayのインデックスを指定するのにもvalarrayを使用することができます。 ただし、インデックスとして使用するvalarrayはsize_t型である必要があります。

mask_arrayクラス

mask_arrayクラスの機能は、indirect_arrayクラスの機能に似ていますが、指定された複数のインデックスではなく、特定の条件にあう要素に対して操作を行います。

#include <iostream>
#include <valarray>

using namespace std;

template <class _T> void EnumerateValarray( valarray<_T>& arr )
{
    size_t i;
    size_t s = arr.size();

    for ( i = 0; i < s; i++ )
    {
        cout << arr[i] << " ";
    }
    cout << endl;
}

int main()
{
    valarray <int> arr(10);
    valarray <int> mask(10);

    size_t i;
    size_t s = arr.size();

    for ( i = 0; i < s; i++ )
    {
        arr[i] = i;
    }

    cout << "arr: "; EnumerateValarray( arr );

    // 5 以上の数を持つ要素全てを 9 に設定する
    mask = arr;
    mask[ mask >= 5 ] = 9;

    cout << "mask : "; EnumerateValarray( mask );

    // 3 以下の数を持つ要素全てを 0 に設定する
    mask = arr;
    mask[ mask <= 3 ] = 0;

    cout << "mask : "; EnumerateValarray( mask );

    // 7 以外の数を持つ要素全てを 0 に設定する
    mask = arr;
    mask[ mask != 7 ] = 0;

    cout << "mask : "; EnumerateValarray( mask );

    return 0;
}
実行結果
arr: 0 1 2 3 4 5 6 7 8 9
mask : 0 1 2 3 4 9 9 9 9 9
mask : 0 0 0 0 4 5 6 7 8 9
mask : 0 0 0 0 0 0 0 7 0 0
Press any key to continue

このサンプルをみてわかるように、mask_arrayはvalarrayの要素の内、比較演算子などを用いて特定の条件を満たす要素に対して操作を行うことができます。

mask_arrayクラスも、indirect_class同様に[]演算子に指定する条件から、内部的・自動的に作成、使用されるので、特別な宣言などを行う必要はありません。

sliceクラス

sliceクラスは少しmask_arrayにも似ていますが、valarray内の値に対して、あるインデックスの範囲を切り出したり、一つおきや複数個おきに取り出すなどの作業を行う機能を提供します。

#include <iostream>
#include <valarray>

using namespace std;

template <class _T> void EnumerateValarray( valarray<_T>& arr )
{
    size_t i;
    size_t s = arr.size();

    for ( i = 0; i < s; i++ )
    {
        cout << arr[i] << " ";
    }
    cout << endl;
}

int main()
{
    valarray <int> arr(20);

    size_t i;
    size_t s = arr.size();

    for ( i = 0; i < s; i++ )
    {
        arr[i] = i;
    }

    cout << "arr: "; EnumerateValarray( arr );

    // インデックス 0 の要素から 2 つおきに 8 項目切り取る
    valarray <int> result1 = arr[ slice( 0, 8, 2 ) ];
    cout << "result1: "; EnumerateValarray( result1 );

    // インデックス 0 の要素から 5 つおきに 4 項目切り取る
    valarray <int> result2 = arr[ slice( 0, 4, 5 ) ];
    cout << "result2: "; EnumerateValarray( result2 );
    
    // インデックス 2 の要素から 4 つおきに 5 項目切り取る
    valarray <int> result3 = arr[ slice( 2, 5, 4 ) ];
    cout << "result3: "; EnumerateValarray( result3 );
    
    return 0;
}
実行結果
arr: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
result1: 0 2 4 6 8 10 12 14
result2: 0 5 10 15
result3: 2 6 10 14 18
Press any key to continue

sliceクラスを利用する場合には、開始インデックス(_StartIndex)、切り取るvalarrayの長さ(_Len)、要素を選択する幅(_Stride)をsliceクラスのコンストラクタに指定してvalarrayから切り出すことができます。

gsliceクラス

gsliceクラスはsliceクラスによく似ていますが、valarrayクラスにより_Lenと_Stribeを指定することで複雑な切り出しを行う機能を提供します。

#include <iostream>
#include <iomanip>
#include <valarray>

using namespace std;

template <class _T> void EnumerateValarrayMatrix( valarray<_T>& arr, size_t m )
{
    size_t i;
    size_t s = arr.size();

    cout << "valarray: ";
    for ( i = 0; i < s; i++ )
    {
        cout << arr[i] << " ";
    }
    cout << endl;

    for ( i = 0; i < s; i++ )
    {
        cout << setw(4) << arr[i];
        if ( ( i + 1 ) % m == 0 ) cout << endl;
    }
    cout << endl;
}

int main()
{
    valarray <int> arr(16);

    size_t i;
    size_t s = arr.size();

    for ( i = 0; i < s; i++ )
    {
        arr[i] = i;
    }

    EnumerateValarrayMatrix( arr, 4 );

    // 長さとなるvalarray
    valarray <size_t> len(2);

    len[0] = 3; // 縦方向
    len[1] = 3; // 横方向

    // 幅となるvalarray
    valarray <size_t> stride(2);

    stride[0] = 4; // 縦方向
    stride[1] = 1; // 横方向

    // 行列状に切り出す
    valarray <int> result = arr[ gslice( 0, len, stride ) ];

    EnumerateValarrayMatrix( result, 3 );


    len[0] = 3; // 縦方向
    len[1] = 2; // 横方向
    stride[0] = 4; // 縦方向
    stride[1] = 1; // 横方向

    result = arr[ gslice( 0, len, stride ) ];
    EnumerateValarrayMatrix( result, 2 );


    len[0] = 2; // 縦方向
    len[1] = 4; // 横方向
    stride[0] = 4; // 縦方向
    stride[1] = 1; // 横方向

    result = arr[ gslice( 0, len, stride ) ];
    EnumerateValarrayMatrix( result, 4 );


    return 0;
}
実行結果
valarray: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
   0   1   2   3
   4   5   6   7
   8   9  10  11
  12  13  14  15

valarray: 0 1 2 4 5 6 8 9 10
   0   1   2
   4   5   6
   8   9  10

valarray: 0 1 4 5 8 9
   0   1
   4   5
   8   9

valarray: 0 1 2 3 4 5 6 7
   0   1   2   3
   4   5   6   7

Press any key to continue

この例では、valarrayを一つの行列のように扱っています。 さらに、gsliceを用いることで、行列の一部分を切り出すような処理を行っています。 また、当然ながら、コンストラクタに渡す_Lenと_Stribeの要素数は同じである必要があります。

このサンプルのように、valarrayのapply()関数に演算を定義した独自のコールバック関数を指定すれば、valarrayに対する演算を定義できます。 この例では加算と減算という単純な演算だけを行わせましたが、複雑な演算を行いたい場合には有用な方法といえます。