今週のプログラムその8

void型関数またはサブルーチンの使い方の続き。 第3回で作った行列を扱うプログラムをvoid型関数(サブルーチン) を使って書きかえてみる。

解説

  1. 前回も書いたとおり、配列は関数(サブルーチン)に”実体”として 受け渡されるので、 関数(サブルーチン)中で値を変更すれば、その結果が元のプログラムにも 反映される。 これを利用して、”その3”で作った行列のプログラムを void型関数(サブルーチン)で書き換えてみる。 program 18はprogram 7よりずいぶん長くなってしまって、”なんだこりゃ”と 思うかもしれないけど、program 18で用意した関数(サブルーチン)を そのまま使うことによって、program 19がずいぶんと簡単に書けてしまったこと に注目してください。

  2. FORTRANのサブルーチン(関数でも)は、配列の大きさも受け渡せるので
    
           program main
           real x(10,10)
           call aaa(x,10)
           end
    
           subroutine aaa(a,n)
           real a(n,n)
            以下省略
    
    などという書き方ができる。この場合、配列の大きさはmainの中でだけ 確定させておけばよい
  3. Cでは、一次元配列なら大きさを指定せずに関数に渡せるが、 二次元配列の場合、二次元目の大きさだけは関数の中で確定しておかなくては ならないことに注意


まずはC版

program 18


/* 行列とベクトルの掛け算 
 program 7を書き換える。掛け算部分と
 行列への入力や出力をvoid型関数にする。
 また、行列の大きさはdefineで指定する
*/
#include<stdio.h>
#define NN 2              行列・ベクトルの大きさを指定

void m_times_v(double [][],double [],double[]); 五つの関数のプロトタイプ宣言
void m_input(double [][]);
void v_input(double []);
void m_output(double [][]);
void v_output(double []);

main()
{
  double a[NN][NN],x[NN],y[NN];
  int i,j;

/*  キーボード(またはファイル)から行列に値を読み込む */
  m_input(a);

/*  ベクトルに値を読み込む */
  v_input(x); 

/* y = ax を計算*/
  m_times_v(a,x,y);

/* 出力  */
  printf("matrix a:\n");
  m_output(a);
  printf("vector x:\n");
  v_output(x);
  printf("vector y=ax:\n");
  v_output(y);
}                                       mainはここまで

/* ベクトルw = 行列m * ベクトルv を計算する関数*/
 void m_times_v(double m[][NN],double v[],double w[])
{
 int i,j;

/*  ベクトルwを初期化(値を0に) */
  for( i=0; i<NN; ++i){
     w[i]=0;
  }    

/*  m*vによりwを求める */
  for( i=0; i<NN; ++i){
    for( j=0; j<NN; ++j){
       w[i] += m[i][j]*v[j];
    }
  }
}

/* ベクトル(一次元配列)の値をキーボード(またはファイル)から読み込む関数 */
void v_input(double v[])
{
  int i;

  for( i=0; i<NN; ++i){
      scanf("%lf",&v[i]);
    }
}

/* 行列(二次元配列)の値をキーボード(またはファイル)から読み込む関数 */
void m_input(double m[][NN])
{
  int i,j;
  for( i=0; i<NN; ++i){
    for( j=0; j<NN; ++j){
      scanf("%lf",&m[i][j]);
    }
  }
}

/* ベクトル(一次元配列)の値を出力する関数 */
void v_output(double v[])
{
 int i;
  for( i=0; i<NN; ++i){
    printf("  %f\n",v[i]);
  }
}

/* 行列(二次元配列)の値を出力する関数 */
void m_output(double m[][NN])
{
  int i,j;
  for( i=0; i<NN; ++i){
    for( j=0; j<NN; ++j){ 
      printf("  %f",m[i][j]);
    }
    printf("\n"); 
  }
}

program 19


/* 行列同士の掛け算 
 program 8を書き換える。
 新しく作る関数は掛け算部分だけ。
 行列への入力や出力はprogram 18で作った関数を使う。
 program 18からm_inputとm_outputをコピーして、このプログラムの
最後につけてください
*/
#include<stdio.h>
#define NN 2                   行列の大きさを指定

void m_times_m(double [][],double [][],double[][]);  三つの関数のプロトタイプ宣言
void m_input(double [][]);
void m_output(double [][]);

main()
{
  double a[NN][NN],b[NN][NN],c[NN][NN];

/*  キーボード(またはファイル)から行列に値を読み込む */
  m_input(a);
  m_input(b);

/* c = ab を計算*/
  m_times_m(a,b,c);

/* 出力  */
  printf("matrix a:\n");
  m_output(a);
  printf("matrix b:\n");
  m_output(b);
  printf("matrix c=ab:\n");
  m_output(c);
}                                               mainはここまで

/* 行列m3 = 行列m1 * 行列m2 を計算する関数*/
 void m_times_m(double m1[][NN],double m2[][NN],double m3[][NN])
{
 int i,j,k;

/*  行列m3を初期化(値を0に) */
  for( i=0; i<NN; ++i){
    for( j=0; j<NN; ++j){ 
      m3[i][j]=0;
    }
  }

/* m1*m2によりm3を求める */
  for( i=0; i<NN; ++i){
    for( j=0; j<NN; ++j){
      for( k=0; k<NN; ++k){
        m3[i][j] += m1[i][k]*m2[k][j];
      }
    }
  }
}

FORTRAN版

program 18


C 行列とベクトルの掛け算
      program main
      parameter(NN=2)              配列の大きさをparameterで指定
      real*8 a(NN,NN),x(NN),y(NN)

C  行列に値をいれる(値自身は意味ないです)
      call m_input(a,NN)

C  ベクトルに値をいれる
      call v_input(x,NN)

C  y=a*xを計算
      call m_times_v(a,x,y,NN)

C  出力
      write(*,*)"matrix a:"
      call m_output(a,NN)
      write(*,*)"vector x:"
      call v_output(x,NN)
      write(*,*)"vector y=ax:"
      call v_output(y,NN)

      end                               mainはここまで

C ベクトルw = 行列m * ベクトルv を計算するサブルーチン
      subroutine m_times_v(m,v,w,n)
      real*8 m(n,n),v(n),w(n)

C  ベクトルwを初期化(値を0に)
      do i=1,n
        w(i)=0 
      enddo

C m*vによりwを求める
      do i=1,n
       do j=1,n               
         w(i)=w(i)+m(i,j)*v(j)
       enddo
      enddo        

      end

C ベクトル(一次元配列)の値をキーボード(またはファイル)
C  から読み込むサブルーチン
      subroutine v_input(v,n)
      real*8 v(n) 

        read(*,*)(v(i),i=1,n)
      end

C 行列(二次元配列)の値をキーボード(またはファイル)
C  から読み込むサブルーチン
      subroutine m_input(m,n)
      real*8 m(n,n) 

       do i=1,n
        read(*,*)(m(i,j),j=1,n)
      enddo
      end

C ベクトル(一次元配列)の値を出力するサブルーチン
      subroutine v_output(v,n)
      real*8 v(n) 

        write(*,*)(v(i),i=1,n)
      end

C 行列(二次元配列)の値を出力するサブルーチン
      subroutine m_output(m,n)
      real*8 m(n,n) 

       do i=1,n
        write(*,*)(m(i,j),j=1,n)
      enddo
      end

program 19


C 行列同士の掛け算 
C program 8を書き換える。
C 新しく作るサブルーチンは掛け算部分だけ。
C 行列への入力や出力はprogram 18で作ったサブルーチンを使う。
C program 18からm_inputとm_outputをコピーして、このプログラムの
C 最後につけてください

      program main
      parameter(NN=2)
      real*8 a(NN,NN),b(NN,NN),c(NN,NN)

C  行列に値をいれる
      call m_input(a,NN)
      call m_input(b,NN)

C  c=a*bを計算
      call m_times_m(a,b,c,NN)

C  出力
      write(*,*)"matrix a:"
      call m_output(a,NN)
      write(*,*)"matrix b:"
      call m_output(b,NN)
      write(*,*)"matrix c=a*b:"
      call m_output(c,NN)

      end                            mainはここまで

C 行列m3 = 行列m1 * 行列m2 を計算するサブルーチン
      subroutine m_times_m(m1,m2,m3,n)
      real*8 m1(n,n),m2(n,n),m3(n,n)

C  m3を初期化(値を0に)
      do i=1,n
        do j=1,n
          m3(i,j)=0 
        enddo
      enddo

C m1*m2によりm3を求める
      do i=1,n
       do j=1,n
        do k=1,n               
         m3(i,j)=m3(i,j)+m1(i,k)*m2(k,j)
        enddo 
       enddo
      enddo        

      end