オブジェクト間の演算
行列の処理と演算例
関数値としてクラス型を返すと、オブジェクト間の演算を表すことができる。行列をクラス型で表して、行列の処理および行列間の演算を表す関数を作成してみた。行列についての簡潔な解説は、<岡本安晴「統計学を学ぶための数学入門[下]培風館」>などを参照されたい。
行列を、以下のように宣言した。
public class MyMat {
public
int nr = 0, nc = 0; // nr: 行数 nc: 列数
public
double mat[][] = null; // 行列の要素用配列
public
MyMat(){}
// 続きは、このウェブサイト後半に掲載
この行列のクラス型を用いたプログラム例を以下のように作成した。
package CheckMat;
import MyMat.*;
import MyMatExcept.*;
public class CheckMat {
public
static void main(String args[]){
// 例外を生成する機能のある関数を用いているので、 try 文で囲む必要がある。
try{
double
a[][] = {{1,2},{3,4}};
double
b[][] = {{10,20},{30,40}};
MyMat.ShowArray(a,
"a =" );
MyMat.ShowArray(b,
"b =" );
MyMat
ma = new MyMat(a);
MyMat
mb = new MyMat(b);
MyMat
msum = new MyMat();
msum
= ma.Add(mb); // msum
= ma + mb
MyMat.ShowMat(msum,
"Add");
MyMat
msum1 = new MyMat();
msum1.SetMyMat(ma.Add(mb)); // msum1
<- (ma + mb)
MyMat.ShowMat(msum1,
"SetMyMat");
//
// msum
を、配列に変換して、表示。 こういうこともできるという単なる例。
//
MyMat.ShowArray(MyMat.MyMatToArray(msum),
"Show Array =");
MyMat
mc = new MyMat();
mc
= ma.Scalar(100.0); // mc
= 100.0 * ma
MyMat.ShowMat(mc, "ma * 100 =");
double
d[][] = {{1, 2}, {3, 4}, {5, 6}};
MyMat
md = new MyMat(d);
MyMat.ShowMat(md,
"md =");
MyMat.Transpose(md); // md
を転置する。 md <- md'
MyMat.ShowMat(md, "md' =");
}
catch(MyMatExcept
e){
System.out.println(e.str);
}
catch(Exception
e){
System.out.println("An
exception was thrown.");
System.out.println(e.toString());
}
}
}
プログラムをまとめた圧縮ファイルsrc_MyMat.zipは、圧縮ファイル名src_MyMat.zipをクリックしてダウンロード・解凍(展開)して利用することができる。利用は。利用者の責任において、自由にしてください。ダウンロードしたパケットファイルの利用法は、このウェブサイトで説明している。
実行結果を図1に示す。
図1
行列を表すクラス型の宣言は、次のコードで行われている。
package MyMat;
import MyMatExcept.*;
//
// 行列演算のためのクラス型
//
public class MyMat {
public
int nr = 0, nc = 0; // nr: 行数 nc: 列数
public
double mat[][] = null; // 行列の要素用配列
public
MyMat(){}
public
MyMat(int vnr, int vnc) throws MyMatExcept{
if
((vnr < 1) || (vnc < 1)){
MyMatExcept
e = new MyMatExcept();
e.str
= "Invalid size number...(" + vnr + ", " + vnc +
")";
throw
e;
}
nr
= vnr;
nc
= vnc;
mat
= new double[nr][nc];
}
//
// 配列 a を用いてクラス型 MyMat を作成
//
public
MyMat( double a[][]){
nr
= a.length;
nc
= a[0].length;
mat
= new double[nr][nc];
for
(int i = 0; i < nr; i++)
for
(int j = 0; j < nc; j++)
mat[i][j]
= a[i][j];
}
//
// コピーコンストラクタ
//
public
MyMat( MyMat a){
nr
= a.nr;
nc
= a.nc;
mat
= new double[nr][nc];
for
(int i = 0; i < nr; i++)
for
(int j = 0; j < nc; j++)
mat[i][j]
= a.mat[i][j];
}
//
// 配列 a[][] をクラス型の変数に設定
//
public
void setMat( double a[][]){
nr
= a.length;
nc
= a[0].length;
mat
= new double[nr][nc];
for
(int i = 0; i < nr; i++)
for
(int j = 0; j < nc; j++)
mat[i][j]
= a[i][j];
}
//
// 第 i 行、第
j 列の要素の値を返す
//
public
double cell(int pr, int pc) throws MyMatExcept {
if
((pr < 0) || (pr >= nr) || (pc < 0) || (pc >= nc)){
MyMatExcept
e = new MyMatExcept();
e.str
= "In valid cell position...(" + pr + ", " + pc +
")";
throw
e;
}
return
mat[pr][pc];
}
//
// 行列の要素をスカラー s 倍した行列を関数値として返す
//
public
MyMat Scalar(double s){
MyMat
t = new MyMat();
t.nr
= nr;
t.nc
= nc;
t.mat
= new double[nr][nc];
for
(int i = 0; i < nr; i++)
for
(int j = 0; j < nc; j++)
t.mat[i][j]
= s * mat[i][j];
return
t;
}
//
// 行列の和を関数値として返す。
//
public
MyMat Add (MyMat b) throws MyMatExcept{
if
((nr != b.nr) || (nc != b.nc)){
MyMatExcept
e = new MyMatExcept();
e.str
= "In Add method, The sizes do not match...(" + nr + ", " +
nc + ") vs (" + b.nr + ", " + b.nc + ")";
throw
e;
}
MyMat
t = new MyMat(nr, nc);
for
(int i = 0; i < nr; i++)
for
(int j = 0; j < nc; j++)
t.mat[i][j]
= mat[i][j] + b.mat[i][j];
return
t;
}
//
// 行列 a を自身にコピーする
//
public
void SetMyMat(MyMat a){
nr
= a.nr;
nc
= a.nc;
mat
= new double[nr][nc];
for
(int i = 0; i < nr; i++)
for
(int j = 0; j < nc; j++)
mat[i][j]
= a.mat[i][j];
}
/**************************************************************
static関数は、「クラス型名.関数名」の形式で呼び出す。
**************************************************************/
//
// 文字列 str の出力後、 行列 a の内容を出力する。
//
public
static void ShowMat( MyMat a, String str ){
System.out.println();
System.out.println(str);
for
(int i = 0; i < a.nr; i++){
for
(int j = 0; j < a.nc; j++)
System.out.print(" " + a.mat[i][j]);
System.out.println();
}
}
//
// 文字列 str の出力後、配列 a[][] の内容を出力する。
//
public
static void ShowArray( double a[][], String str ){
int
r = a.length;
int
c = a[0].length;
System.out.println();
System.out.println(str);
for
(int i = 0; i < r; i++){
for
(int j = 0; j < c; j++)
System.out.print(" " + a[i][j]);
System.out.println();
}
}
//
// 行列 a を配列
double[][] に変換したものを、関数値として返す。
//
public
static double[][] MyMatToArray(MyMat a){
double[][]
t = new double[a.nr][a.nc];
for
(int i = 0; i < a.nr; i++)
for
(int j = 0; j < a.nc; j++)
t[i][j]
= a.mat[i][j];
return
t;
}
//
// 引数、行列 a 、の転置行列を、引数 a に設定して返す。
//
public
static void Transpose( MyMat a ){
MyMat
t = new MyMat(a);
a.mat
= new double[a.nc][a.nr];
a.nr
= t.nc;
a.nc
= t.nr;
for
(int i = 0; i < a.nr; i++)
for
(int j = 0; j < a.nc; j++)
a.mat[i][j]
= t.mat[j][i];
}
}
行列を表すクラス型で用いられている例外型の宣言は、以下のようになっている。
package MyMatExcept;
public class MyMatExcept extends
Exception {
public
String str;
public
MyMatExcept(){}
public
MyMatExcept(String vstr){
str
= vstr;
}
public
void SetStr( String v_str ){
str
= v_str;
}
}
関数(メソッド)の引数は、intやdoubleなどの単純型の場合は、値がコピーされて関数内で使われるので、関数内で値を変えても呼び出し元の値は変わらない。しかし、単純型以外の場合は、引数は参照(実引数を指し示すもの)であるので、関数内では、引数は実引数と同じものを指し示すものとして、呼び出し元の実引数であるオブジェクトを表している。したがって、関数内で値を変えると、それは呼び出し元の実引数に反映される。このことを示す簡単なサンプルプログラムとその実行結果を図2に示す
図2
代入演算子 = の働きも変数が単純型であるかないかによって異なる。整数型 int のように単純型同志の代入は、右辺の値の左辺の変数への代入である。しかし、単純型でないときは、変数はオブジェクトを指し示すものであるので、代入は「指し示す先(どこを指し示しているか)」の代入となり、左辺の変数が指し示すものが右辺の指し示すものと同じものになることを意味する。単純型でない変数は、C/C++におけるポインタ、あるいはVC++/CLIにおけるハンドルと同様の働きをする。簡単な確認プログラムとその実行結果を図3に示す。
図3
整数型 int 型の変数 a と b の代入 b = a; では、変数 b への演算は変数 a に影響しない。
単純型(int 型)の配列型 c[] と d[] での代入 d = c; では、配列型の変数 d は配列型の変数 c と同じものを指し示す(参照)ことになるので、d[0] への演算は c[0] に反映される。配列 d と c は同じ配列を指し示しているからである。
これに対して、
int e[] = { 1 };
int f[] = new int[1];
では、配列型の変数 e と f は、異なる配列型のオブジェクトを指し示しているので、f[0] = e[0]; と変数 e の指し示す配列型の要素 e[0] を変数 f の指し示す配列型の要素 f[0] に代入した後、 f[0] に演算を行ってもそれは e[0] に影響しない。
関数値も、単純型でない場合は、その値(オブジェクト)そのものではなく、その指し示す先を示す値を返す(参照)が、そのことを示すプログラム例と実行結果を図4に示す。
図4
最初、配列 a と b は
int a[] = { 1 };
int b[] = { 2 };
と、お互いに別な配列として宣言されている。しかし、関数 func を
b = func(a);
と呼び出すことにより、配列型変数 b は a の指し示す配列を指し示すようになる。このことを,以下のコード
a[0] = 10;
System.out.println("a[0] = " + a[0] + " b[0] = " + b[0]);
b[0] = 100;
System.out.println("a[0] = " + a[0] + " b[0] = " + b[0]);
の実行によって確認している。