C++編【標準ライブラリ】 第33章 valarray

このエントリーをはてなブックマークに追加

この章の概要

この章の概要です。

valarray

valarray は、数値演算に特化した一次元の配列を表現するクラステンプレートで、 複数の要素に対して、同時に同じ操作(計算)を行うことを効率的に行えるように設計されています。
使用するには、valarray という名前の標準ヘッダをインクルードする必要があります。

valarray は、次のように定義されています。

namespace std {
    template <typename T>
    class valarray {
    };
}

テンプレートパラメータ T は、要素の型を指定します。 valarray自体が、数値演算に特化したものなので、普通は数値型を指定することになるはずです。

生成と初期化

valarray には、複数のコンストラクタが定義されているので、様々な方法でインスタンス化できます。

valarray();
explicit valarray(std::size_t count);
valarray(const T& value, std::size_t count);
valarray(const T* values, std::size_t count);
valarray(const valarray& v);
valarray(const slice_array<T>& v);
valarray(const gslice_array<T>& v);
valarray(const mask_array<T>& v);
valarray(const indirect_array<T>& v);

1つ目の形式は、空の valarray を生成します。 この場合、後から resizeメンバ関数を呼び出して、要素を割り当てます。

2つ目の形式は、指定した個数の要素を含んだ valarray を生成します。 各要素は、デフォルトコンストラクタによって初期化されます。

3つ目の形式も、指定した個数の要素を含んだ valarray を生成しますが、各要素の初期値も指定します。 vector などの STLコンテナにも、同じ意味合いのコンストラクタがありますが、 引数の順番が逆になっていることに注意して下さい。

4つ目の形式は、指定した配列の要素を使って valarray を生成します。 引数 values がヌルポインタで無いことや、指し示す先に、引数count で指定した値以上の要素があることについては、 プログラマ側で保証する必要があります。

5つ目の形式は、コピーコンストラクタです。

6つ目以降の形式は、valarray に関連する各種クラステンプレートから生成するためのものです。 これについては、「サブセット」の項で取り上げます。

C++11 (生成と初期化)

C++11 で、以下のコンストラクタが追加されています。

valarray(valarray&& v);
valarray(std::initializer_list<T> lst);

VisualC++2013/2015/2017、clang 3.7 のいずれでも対応しています。

要素数

valarray は、内部的に動的な一次元配列を持っています。 この要素数を指して、valarray の要素数と呼ぶことにします。

valarray の現在の要素数は、sizeメンバ関数で取得できます。

std::size_t size() const;

valarray同士で何らかの操作を行う場合に、互いの要素数が一致していないと、未定義な動作になるので、 要素数には注意を払って下さい。 また、当然ながら、[]演算子を使って要素にアクセスする際にも、 要素数以上のインデックスを使用すると、未定義な動作になります

resizeメンバ関数を使うと、現在の要素数を変更できます。

void resize(std::size_t count, T value = T());

要素数が、第1引数で指定した値になります。
元の要素数よりも大きくなる場合は、新しい要素は第2引数の値で初期化されます。 第2引数を指定しなかった場合は、デフォルトコンストラクタで初期化されます。
内部の配列自体が作り替えられる可能性があるため、 要素を指すポインタや参照の類は無効になることに注意して下さい

演算子

valarray に対して使用できる演算子が幾つか定義されています。

[]演算子

valarray は一次元配列を模しているので、[]演算子を使って各要素にアクセスできます。 有効範囲外のインデックスを指定すると、未定義な動作になります。

#include <iostream>
#include <valarray>

namespace {
	void PrintValarray(const std::valarray<double>& va)
	{
		std::cout.setf(std::ios_base::showpoint);

		const std::size_t size = va.size();
		for (std::size_t i = 0; i < size; ++i) {
			if (i >= 1) {
				std::cout << ", ";
			}
			std::cout << va[i];
		}
		std::cout << std::endl;
	}
}

int main()
{
	std::valarray<double> va(3);

	va[0] = 5.0;
	va[1] = 4.5;
	va[2] = -5.0;

	PrintValarray(va);
}

実行結果

5.00000, 4.50000, -5.00000

[]演算子は、サブセットを得る操作を行う際にも使用されます。 これについては、「サブセット」の項で取り上げます。

単項演算子

以下の各種単項演算子を適用できます。

valarray の全要素に対して、これらの演算子を適用し、結果の valarray を返します。

#include <iostream>
#include <valarray>

namespace {
	void PrintValarray(const std::valarray<double>& va)
	{
		std::cout.setf(std::ios_base::showpoint);

		const std::size_t size = va.size();
		for (std::size_t i = 0; i < size; ++i) {
			if (i >= 1) {
				std::cout << ", ";
			}
			std::cout << va[i];
		}
		std::cout << std::endl;
	}
}

int main()
{
	std::valarray<double> va(3);

	va[0] = 5.0;
	va[1] = 4.5;
	va[2] = -5.0;

	PrintValarray(-va);
}

実行結果

-5.00000, -4.50000, 5.00000

!演算子だけはやや特殊で、valarray の各要素に対して !演算子を適用した結果を、対応するインデックスに格納して返却します。 そのため、!演算子の戻り値の型は valarray<bool> になります。

二項演算子と、その複合代入演算子

以下の各種二項演算子を適用できます。

オペランドは、2つとも valarray にするか、一方を T型(T はテンプレートパラメータ)の値にできます。

2つとも valarray にした場合は、互いの同じインデックスにある要素同士に対する演算になります。 そのため、2つのオペランドを valarray にする場合は、両者の要素数が一致していないと未定義の動作になります。

一方を valarray、他方を T型の値にした場合は、valarray の全要素に対して、T型の値を使って演算子を適用します。 例えば、va という要素数が 3 の int型の valarray に対して「va * 2」とすることは、「va[0] * 2, va[1] * 2, va[2] * 2」とすることに等しいです。 ただし、計算の順番は保証されません。

なお、これらの二項演算子に対応する、複合代入演算子も使用できます。

#include <iostream>
#include <valarray>

namespace {
	void PrintValarray(const std::valarray<double>& va)
	{
		std::cout.setf(std::ios_base::showpoint);

		const std::size_t size = va.size();
		for (std::size_t i = 0; i < size; ++i) {
			if (i >= 1) {
				std::cout << ", ";
			}
			std::cout << va[i];
		}
		std::cout << std::endl;
	}
}

int main()
{
	std::valarray<double> va1(5.0, 3);
	std::valarray<double> va2(2.0, 3);

	PrintValarray(va1 + va2);
	PrintValarray(va1 * 3.0);

	va1 -= va2;
	PrintValarray(va1);

	va2 -= 0.5;
	PrintValarray(va2);
}

実行結果

7.00000, 7.00000, 7.00000
15.0000, 15.0000, 15.0000
3.00000, 3.00000, 3.00000
1.50000, 1.50000, 1.50000

比較演算子、論理演算子

以下の比較演算子、論理演算子を適用できます。

二項演算子の場合と同じく、2つのオペランドは、ともに要素数が同じ valarray か、 他方に T型の値を与えるかのいずれかになります。
前者の場合は、互いの同じインデックスにある要素同士に対する演算になります。 後者の場合は、全要素に対して、T型の値を使って演算子を適用します。

いずれの演算子も、戻り値の型は valarray<bool> になります。 つまり、各要素に対する演算子の適用結果を、対応するインデックスに格納して返却します。

#include <iostream>
#include <valarray>

namespace {
	void PrintValarray(const std::valarray<bool>& va)
	{
		std::cout.setf(std::ios_base::boolalpha);

		const std::size_t size = va.size();
		for (std::size_t i = 0; i < size; ++i) {
			if (i >= 1) {
				std::cout << ", ";
			}
			std::cout << va[i];
		}
		std::cout << std::endl;
	}
}

int main()
{
	std::valarray<double> va1(3);
	va1[0] = 3.0;
	va1[1] = 4.0;
	va1[2] = 5.0;

	std::valarray<double> va2(3.0, 3);

	PrintValarray(va1 == va2);
	PrintValarray(va1 != va2);
	PrintValarray(va1 > va2);

	PrintValarray(va1 == 5.0);
	PrintValarray(va1 != 5.0);
	PrintValarray(va1 > 5.0);
}

実行結果

true, false, false
false, true, true
false, true, true
false, false, true
true, true, false
false, false, false

これらの演算子が返す結果には不満を感じる場面があるかも知れません。 例えば、2つの valarray の要素がすべて一致しているかどうかは、「va1 == va2」だけでは判定できず、 返された valarray<bool> の要素がすべて true かどうかを調べる必要があります。 そのため、補助関数を用意しておくと良いかも知れません。

#include <iostream>
#include <valarray>

namespace {
	bool IsAllTrue(const std::valarray<bool>& va)
	{
		const std::size_t size = va.size();
		for (std::size_t i = 0; i < size; ++i) {
			if (!va[i]) {
				return false;
			}
		}
		return true;
	}
}

int main()
{
	std::valarray<double> va1(3);
	va1[0] = 3.0;
	va1[1] = 4.0;
	va1[2] = 5.0;

	std::valarray<double> va2(3.0, 3);

	std::cout << std::boolalpha
	          << IsAllTrue(va1 == va1) << "\n"
	          << IsAllTrue(va1 == va2) << "\n"
	          << IsAllTrue(va2 == 3.0) << std::endl;
}

実行結果

true
false
true

C++11以降であれば、IsAllTrue関数は、次のように1行で書けます。
「return std::all_of(std::cbegin(va), std::cend(va), [](bool b){return b;});」

メンバ関数による操作

valarray には、すべての要素を対象にした操作を行うメンバ関数が用意されています。

最小値・最大値

minメンバ関数は、すべての要素の中で、最小の値を返します。 maxメンバ関数は、すべての要素の中で、最大の値を返します。

T min() const;
T max() const;

いずれも、valarray が要素を1つも持っていない場合には、未定義の動作になることに注意して下さい。 また、minメンバ関数は大小関係の比較のために T型 に対する <演算子を、maxメンバ関数は >演算子を用います

合計

sumメンバ関数は、すべての要素の値の合計を返します。

T sum() const;

valarray が要素を1つも持っていない場合には、未定義の動作になることに注意して下さい。 また、合計値を求めるために、T型に対する +=演算子を使用しています

シフト

shiftメンバ関数は、要素の位置を指定した個数分だけシフトさせます。

valarray<T> shift(int count) const;

引数 count の値が正数の場合は左シフト(配列の先頭に向かってシフト)になり、 負数の場合は右シフト(配列の末尾に向かってシフト)になります。 シフトした結果、空いた位置には、デフォルトコンストラクタによって初期化された新しい要素が作られます

constメンバ関数であることから分かるように、対象の valarray自身は変化しません。 シフトした後の新しい valarray が戻り値で返されるので、これを受け取る必要があります。

#include <iostream>
#include <valarray>

namespace {
	void PrintValarray(const std::valarray<double>& va)
	{
		std::cout.setf(std::ios_base::showpoint);

		const std::size_t size = va.size();
		for (std::size_t i = 0; i < size; ++i) {
			if (i >= 1) {
				std::cout << ", ";
			}
			std::cout << va[i];
		}
		std::cout << std::endl;
	}
}

int main()
{
	std::valarray<double> va(5);
	va[0] = 3.0;
	va[1] = 4.0;
	va[2] = 5.0;
	va[3] = 6.0;
	va[4] = 7.0;

	PrintValarray(va);

	va = va.shift(2);
	PrintValarray(va);
	
	va = va.shift(-1);
	PrintValarray(va);
}

実行結果

3.00000, 4.00000, 5.00000, 6.00000, 7.00000
5.00000, 6.00000, 7.00000, 0.000000, 0.000000
0.000000, 5.00000, 6.00000, 7.00000, 0.000000

循環シフト

cshiftメンバ関数は、要素の位置を指定した個数分だけ循環シフトさせます。

valarray<T> cshift(int count) const;

引数 count の値が正数の場合は左シフト(配列の先頭に向かってシフト)になり、 負数の場合は右シフト(配列の末尾に向かってシフト)になります。
循環シフトなので、シフトによって配列の範囲外へ飛び出していった要素は、逆側から戻ってきます。 そのため、要素に空きが生まれることはありません。

constメンバ関数であることから分かるように、対象の valarray自身は変化しません。 シフトした後の新しい valarray が戻り値で返されるので、これを受け取る必要があります。

#include <iostream>
#include <valarray>

namespace {
	void PrintValarray(const std::valarray<double>& va)
	{
		std::cout.setf(std::ios_base::showpoint);

		const std::size_t size = va.size();
		for (std::size_t i = 0; i < size; ++i) {
			if (i >= 1) {
				std::cout << ", ";
			}
			std::cout << va[i];
		}
		std::cout << std::endl;
	}
}

int main()
{
	std::valarray<double> va(5);
	va[0] = 3.0;
	va[1] = 4.0;
	va[2] = 5.0;
	va[3] = 6.0;
	va[4] = 7.0;

	PrintValarray(va);

	va = va.cshift(2);
	PrintValarray(va);
	
	va = va.cshift(-1);
	PrintValarray(va);
}

実行結果

3.00000, 4.00000, 5.00000, 6.00000, 7.00000
5.00000, 6.00000, 7.00000, 3.00000, 4.00000
4.00000, 5.00000, 6.00000, 7.00000, 3.00000

関数呼び出しの適用

applyメンバ関数は、すべての要素に対して、指定の関数を適用します。

valarray<T> apply(T func(T)) const;
valarray<T> apply(T func(const T&)) const;

各要素を elem と表現すると、すべての要素に対して「func(elem)」を呼び出し、 その結果を新しい valarray に格納します。 最終的に、すべての結果を格納した valarray が戻り値で返されます。

#include <iostream>
#include <valarray>

namespace {
	void PrintValarray(const std::valarray<double>& va)
	{
		std::cout.setf(std::ios_base::showpoint);

		const std::size_t size = va.size();
		for (std::size_t i = 0; i < size; ++i) {
			if (i >= 1) {
				std::cout << ", ";
			}
			std::cout << va[i];
		}
		std::cout << std::endl;
	}
}

int main()
{
	std::valarray<double> va(5);
	va[0] = 3.7;
	va[1] = -1.9;
	va[2] = 2.5;
	va[3] = 0.6;
	va[4] = -4.0;

	PrintValarray(va.apply(std::floor));
	PrintValarray(va.apply(std::ceil));
	PrintValarray(va.apply(std::round));
}

実行結果

3.00000, -2.00000, 2.00000, 0.000000, -4.00000
4.00000, -1.00000, 3.00000, 1.00000, -4.00000
4.00000, -2.00000, 3.00000, 1.00000, -4.00000

数学系関数

以下の数学系関数が、引数と戻り値に valarray を取る形で定義されています。 これらはメンバ関数ではなく、std名前空間内の通常の関数です。

template <typename T> valarray<T> abs(const valarray<T>& va);
template <typename T> valarray<T> exp(const valarray<T>& va);
template <typename T> valarray<T> log(const valarray<T>& va);
template <typename T> valarray<T> log10(const valarray<T>& va);
template <typename T> valarray<T> pow(const valarray<T>& base, const valarray<T>& exp);
template <typename T> valarray<T> pow(const valarray<T>& base, const T& vexp);
template <typename T> valarray<T> pow(const T& vbase, const valarray<T>& exp);
template <typename T> valarray<T> sqrt(const valarray<T>& va);
template <typename T> valarray<T> sin(const valarray<T>& va);
template <typename T> valarray<T> cos(const valarray<T>& va);
template <typename T> valarray<T> tan(const valarray<T>& va);
template <typename T> valarray<T> sinh(const valarray<T>& va);
template <typename T> valarray<T> cosh(const valarray<T>& va);
template <typename T> valarray<T> tanh(const valarray<T>& va);
template <typename T> valarray<T> asin(const valarray<T>& va);
template <typename T> valarray<T> acos(const valarray<T>& va);
template <typename T> valarray<T> atan(const valarray<T>& va);
template <typename T> valarray<T> atan2(const valarray<T>& y, const valarray<T>& x);
template <typename T> valarray<T> atan2(const valarray<T>& y, const T& vx);
template <typename T> valarray<T> atan2(const T& vy, const valarray<T>& x);

これらの関数は、C言語でも同名の関数が存在しているので、詳しいことは省きます。
ここまでに説明してきた通り、valarray同士で処理が行われるものは、同じインデックスの要素同士で、 valarray と T型の値で処理が行われるものは、すべての要素に対して、1つの値を使って同じ処理が行われます。

なお、引数に2つの valarray を渡す場合は、要素数が同じでなければ未定義の動作になります

サブセット

valarray の要素の幾つかを選び出して、新たな valarray を得ることができます。
例えば、「1, 2, 3, 4, 5」という5つの要素で構成されている valarray から、 「値が偶数の要素」という条件で、「2, 4」だけを含んだ valarray を簡単に得られるようになっています。 このように得られる新たな valarray をサブセットと呼びます。 元になった valarray から要素が無くなる訳ではありません。

サブセットを得るためには幾つかの方法があるので、順番に説明していきます。

スライス

要素の開始位置、個数、間隔(ストライド)という3つの指定によって、サブセットを得る方法をスライスと言います。 これを行うには、std::sliceクラスと、[]演算子を使用します。

#include <iostream>
#include <valarray>

namespace {
	void PrintValarray(const std::valarray<double>& va)
	{
		std::cout.setf(std::ios_base::showpoint);

		const std::size_t size = va.size();
		for (std::size_t i = 0; i < size; ++i) {
			if (i >= 1) {
				std::cout << ", ";
			}
			std::cout << va[i];
		}
		std::cout << std::endl;
	}
}

int main()
{
	std::valarray<double> va1(5);
	std::valarray<double> va2(5);
	va1[0] = 0.0;
	va1[1] = 1.0;
	va1[2] = 2.0;
	va1[3] = 3.0;
	va1[4] = 4.0;
	va2[0] = 2.0;
	va2[1] = -1.0;
	va2[2] = -2.0;
	va2[3] = -1.0;
	va2[4] = 2.0;

	PrintValarray(va1[std::slice(0, 3, 2)]);
	PrintValarray(va1[std::slice(1, 2, 2)]);
	PrintValarray(va1[std::slice(4, 3, -2)]);

	va1[std::slice(0, 3, 2)] = 2.0;
	PrintValarray(va1);

	va1[std::slice(0, 3, 2)] *= va2[std::slice(0, 3, 2)];
	PrintValarray(va1);
}

実行結果

0.000000, 2.00000, 4.00000
1.00000, 3.00000
4.00000, 2.00000, 0.000000
2.00000, 1.00000, 2.00000, 3.00000, 2.00000
4.00000, 1.00000, -4.00000, 3.00000, 4.00000

std::slice の使い方が、関数呼び出しのように見えますが、これは一時オブジェクトを作って、コンストラクタに引数を渡しています。
第1引数が、サブセットに選び出す要素の開始インデックス、 第2引数が、選び出す要素の個数、 第3引数が、選び出す各要素間の距離です。これは負数にもできます。
なお、こうして得られる対象要素のインデックスは、すべて有効なもので無ければなりません。 存在しないインデックスを指定してしまうと、未定義の動作になります

[]演算子は、std::slice型を受け取れるようにオーバーロードされており、 スライスによってサブセットを定義するように処理が行われます。

std::valarray<T>    operator[](std::slice slicearr) Kconst;
std::slice_array<T> operator[](std::slice slicearr);

非constメンバ関数の場合は、std::slice_arrayクラステンプレートが返されます。 これは、元の valarray への操作を行えるようにするための補助クラスです。 主に代入系の演算子を使用でき、その演算は、元の valarray に対して行われます。
std::slice_array を経由して処理が行えない場合には、std::valarray へキャストして下さい。 std::valarray のコンストラクタには、std::slice_array を渡すタイプのものがあるので(「生成と初期化」の項参照)、static_cast を行うだけで変換できます。

汎用スライス

スライスをより汎用的にした汎用スライスという方法があります。 汎用スライスは、サブセットをn次元の形で得ることができます。 「n=1」であれば、前述のスライスと同様なので、こちらの方が汎用的であるという訳です。

汎用スライスでは、std::gsliceクラス[]演算子を使用します。
汎用スライスの場合には、要素の開始位置、個数、間隔(ストライド)という3つの指定のうち、 個数と間隔を、配列で指定することが可能になっています。

#include <iostream>
#include <valarray>

namespace {
	void PrintValarray(const std::valarray<int>& va)
	{
		const std::size_t size = va.size();
		for (std::size_t i = 0; i < size; ++i) {
			if (i >= 1) {
				std::cout << ", ";
			}
			std::cout << va[i];
		}
		std::cout << std::endl;
	}
}

int main()
{
	std::valarray<int> va(25);
	for (std::size_t i = 0; i < va.size(); ++i) {
		va[i] = i;
	}

	std::size_t numsTable[] = {5, 2};
	std::size_t strideTable[] = {5, 1};
	std::valarray<std::size_t> num(numsTable, 2);
	std::valarray<std::size_t> stride(strideTable, 2);

	PrintValarray(va[std::gslice(1, num, stride)]);
}

実行結果

1, 2, 6, 7, 11, 12, 16, 17, 21, 22

最初に、5x5 の2次元配列をイメージして、valarray を定義し、 ここから、5x2 の範囲の2次元配列を、サブセットとして得ようとしています。

スライスの場合と同じで、今度は std::gslice を []演算子のオペランドとして指定します。 std::gslice の第2、第3引数は、valarray<std::size_t>型で指定します。

valarray<std::size_t>型で指定するために、valarray<std::size_t>型の変数を定義し、 そこに渡す値のために、std::size_t型の配列を定義するという、かなり面倒なコードになっています。 C++11以降であれば、valarray のコンストラクタに std::initializer_list を指定できるので、直接、 「va[std::gslice(1, {5, 2}, {5, 1})]」のような記述が可能です。 これなら煩わしくないし、見やすいですね。

[]演算子は、std::gslice型を受け取れるようにオーバーロードされており、 汎用スライスによってサブセットを定義するように処理が行われます。

std::valarray<T>     operator[](std::gslice gslicearr) Kconst;
std::gslice_array<T> operator[](std::gslice gslicearr);

非constメンバ関数の場合は、std::gslice_arrayクラステンプレートが返されます。 これは、元の valarray への操作を行えるようにするための補助クラスです。 主に代入系の演算子を使用でき、その演算は、元の valarray に対して行われます。
std::gslice_array を経由して処理が行えない場合には、std::valarray へキャストして下さい。 std::valarray のコンストラクタには、std::gslice_array を渡すタイプのものがあるので(「生成と初期化」の項参照)、static_cast を行うだけで変換できます。

マスク

マスクは、valarray<bool> を使って、サブセットを定義する方法です。 true が入っている箇所に対応する要素だけが選び出されます。

#include <iostream>
#include <valarray>

namespace {
	void PrintValarray(const std::valarray<int>& va)
	{
		const std::size_t size = va.size();
		for (std::size_t i = 0; i < size; ++i) {
			if (i >= 1) {
				std::cout << ", ";
			}
			std::cout << va[i];
		}
		std::cout << std::endl;
	}
}

int main()
{
	std::valarray<int> va(25);
	for (std::size_t i = 0; i < va.size(); ++i) {
		va[i] = i;
	}

	PrintValarray(va[va < 10]);
	PrintValarray(va[va % 3 == 0]);
	PrintValarray(va[5 <= va && va <= 15]);
}

実行結果

0, 1, 2, 3, 4, 5, 6, 7, 8, 9
0, 3, 6, 9, 12, 15, 18, 21, 24
5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15

valarray<bool> は、valarray に対して比較演算子や論理演算子を適用すると得られる型と一致しています(「演算子」の項を参照)。 そのため、「va[va < 10]」のような感じで記述できます。

[]演算子は、valarray<bool>型を受け取れるようにオーバーロードされており、 マスクによってサブセットを定義するように処理が行われます。

std::valarray<T>   operator[](valarray<bool>& va) Kconst;
std::mask_array<T> operator[](valarray<bool>& va);

非constメンバ関数の場合は、std::mask_arrayクラステンプレートが返されます。 これは、元の valarray への操作を行えるようにするための補助クラスです。 主に代入系の演算子を使用でき、その演算は、元の valarray に対して行われます。
std::mask_array を経由して処理が行えない場合には、std::valarray へキャストして下さい。 std::valarray のコンストラクタには、std::mask_array を渡すタイプのものがあるので(「生成と初期化」の項参照)、static_cast を行うだけで変換できます。

間接的な指定

valarray<std::size_t> を使って、サブセットを定義する方法もあります。 valarray<std::size_t> には、サブセットとして選び出したい要素のインデックスを格納しておきます。
この方法は、最後に登場する indirect_array というクラステンプレートに倣って、間接的な指定と呼びます。

#include <iostream>
#include <valarray>

namespace {
	void PrintValarray(const std::valarray<int>& va)
	{
		const std::size_t size = va.size();
		for (std::size_t i = 0; i < size; ++i) {
			if (i >= 1) {
				std::cout << ", ";
			}
			std::cout << va[i];
		}
		std::cout << std::endl;
	}
}

int main()
{
	std::valarray<int> va(25);
	for (std::size_t i = 0; i < va.size(); ++i) {
		va[i] = i;
	}

	std::valarray<std::size_t> indexes(5);
	indexes[0] = 7;
	indexes[1] = 15;
	indexes[2] = 7;
	indexes[3] = 10;
	indexes[4] = 4;

	PrintValarray(va[indexes]);
}

実行結果

7, 15, 7, 10, 4

インデックスを格納した std::valarray<std::size_t> には、同じインデックスが含まれても構いませんし、 大小関係がどういう順序で並んでいても構いません。 ただし、無効なインデックスが入っていると、動作は未定義になります。

[]演算子は、valarray<std::size_t>型を受け取れるようにオーバーロードされており、 間接的な指定によってサブセットを定義するように処理が行われます。

std::valarray<T>       operator[](valarray<std::size_t>& va) Kconst;
std::indirect_array<T> operator[](valarray<std::size_t>& va);

非constメンバ関数の場合は、std::indirect_arrayクラステンプレートが返されます。 これは、元の valarray への操作を行えるようにするための補助クラスです。 主に代入系の演算子を使用でき、その演算は、元の valarray に対して行われます。
std::indirect_array を経由して処理が行えない場合には、std::valarray へキャストして下さい。 std::valarray のコンストラクタには、std::mask_array を渡すタイプのものがあるので(「生成と初期化」の項参照)、static_cast を行うだけで変換できます。


練習問題

問題@ valarray に含まれている要素の平均値を求めるプログラムを作成して下さい。

問題A 2つの valarray の内積を求めるプログラムを作成して下さい。

問題B 次のコードで作られる valarray を 5x5 の2次元配列(あるいは行列)と見なしたとき、 左上から右下へ向かう対角線上の要素を、サブセットとして選び出すプログラムを、 スライス、汎用スライス、マスク、間接的な指定の各方法で作成して下さい。

std::valarray<int> va(25);
for (std::size_t i = 0; i < va.size(); ++i) {
	va[i] = i;
}


解答ページはこちら

参考リンク

更新履歴

'2017/3/25 VisualC++ 2017 に対応。

'2017/2/28 新規作成。



前の章へ

次の章へ

C++編のトップページへ

Programming Place Plus のトップページへ