mfcalc.y の改良−行列とその加、減、乗算−

g98p050-1 斎藤史子

出題日:5 月 21 日

1 解説

1.1 改良点

今回の課題は、行列とその加、減、乗算機能をプログラムに組み込むことである。

前回の課題で、組込数を組み込んだが、その値を後から更新することが可能だったため、他の値に変更することができないようにした。

また、 ;(セミコロン) で結果を表示しないようにする機能を新たに追加した。他に、行列は大文字から始まり、 exp 型の変数は小文字から始まるとして、行列を変数に代入できるようにした。さらに、 ’(シングル・クォ−テ−ション) で行列を転置できる機能を追加した。 exp 型演算の履歴 ans と同様にして、最後に行った行列演算の履歴を ANS に格納するようにした。

1.2 詳説

1.2.1 行列の実装とその加、減、乗算

行列の大きさに可変性を持たせるために、それぞれの行列の要素 (a[i,j]) は、同じ行の右となり (a[i+1,j]) にある要素、同じ列の下にある要素 (a[i,j+1]) へのポインタを持たせて、つないだ。 (calc.h:L2:matrix element) ここでは、便宜上、行列リストと呼ぶ。また、行列全体の大きさを把握するため、行列リストの先頭には、行列の行数、列数を格納した。 (calc.h:L10:matr)

また、行列の要素として exp 型を扱うために、 matrix el 型 (new calc.y:L47) を用意し、さらに、行列全体を扱うために、 matrix 型 (new calc.y:L37) を用意した。行列を生成するための関数としては、 matrix el 型の要素をひとつ生成する init matrix(new mfcalc.y:L176)、行方向に要素を追加する wide add matrix (new mfcalc:L204)、列方向に要素を追加する leng add matrix(new mfcalc:L239)、行、列の要素数に不揃いがないか確認する is matrix(new mfcalc:L255) を用意した。

最後に、行列の加、減、乗算は、それぞれ add matrix(new mfcalc:L336),minus matrix(new mfcalc:L377), multi matrix(new mfcalc:L415) で対応した。また、これらの関数内では、指定された大きさの全ての初期要素が 0 である行列を生成する関数 make matr(new mfcalc.y:L313) を用いている。また、行列の転置には、関数 inv matrix(new mfcalc.y:L454) で対応した。

1.2.2 行列の変数と代入

行列の変数として、 MA(new mfcalc:L12) を定義した。そのル−ルは、 exp 型の VAR にならって定義した。また、 MA を記号表に登録するために、 symrec(calc.h:L18) に若干の変更を加えるとともに、行列変数としては最初の文字が大文字であるとして、 yylex(new mfcalc.y:L527) で大文字から始まる変数名は、 MA として認識するようにした。

また、最後に行った行列演算の履歴 ANS(new mfcalc.y:L132) は、 exp 型の ans にならって定義した。

1.2.3 組込み数と結果表示の切替

組込み数が後から変更されることを防ぐために、定数ト−クン CONST (new mfcalc.y:L12) を定義し、ル−ル (new mfcalc.y:L64) から代入文は使えないようにした。また、これについては、 matrix 型についても同様に、ト− クン STMA (new mfcalc.y:L12) を定義した。

また、結果表示の切替については、 line ル−ル (new mfcalc:L31,L33) においては、 ;(セミコロン) が付加される場合は、 printf を呼び出さないようにした。

2 感想

このプログラムには、ひとつの問題点がある。 exp 型の定義されていない変数の値が 0 に初期化されてしまい、エラ−にならない点である。 matrix 型については、定義されていない変数について、 “no matrix” と表示するようにしたので、それに対応するような形にしたい。

また、他にも追加したい機能がいろいろある。 matrix のひとつの要素を用いて、演算等をできるようにすること、行列の大きさを与えれば、ランダムな要素をもつ行列を生成するようにすること、行列を用いて連立一次方程式を解くことなどである。 matrix のひとつの要素の読み出し、ランダムな要素をもつ行列の生成は、比較的に楽に実装できるように思う。また、行列を用いて連立一次方程式を解くことについては、どのアルゴリズムを用いるか、現在考えている。

前回は、どんな機能を追加するか、なかなか思いつかず、苦労したが、今回は、実装してみたいものがいろいろ思いつき、かなり楽しめたので、よかった。

3 ソ−ス

3.1 new mfcalc.y

%{
#include <math.h> /* 数学関数のため */
#include "calc.h" /* 'symrec' の定義を含む */
%}
%union {
  double val; /* 数値を返すため */
  matr *mat;
  matrix_element *mat_el;
  symrec *tptr; /* 記号表へのポインタを返す */
}
%token <val> NUM /* 単純な倍精度数値 */
%token <tptr> VAR CONST FNCT FNCT2 MA STMA /* 変数と関数 */
%type <val> exp
%type <mat> matrix
%type <mat_el> matrix_el
%right '='
%left '-' '+'
%left '*' '/'
%left NEG /* 否定 -- 単項の負 */
%right '^' /* べき乗 */
%left '\'' /* 転置行列 */
%%
input: /* 空 */
       | input line
;
line:   '\n'
| exp '\n' { printf("\t%.10g\n",$1);ans->value.var=$1;}
| exp ';''\n' {ans->value.var=$1;}
| matrix '\n' {matrix_print($1);ANS->value.mat=$1;}

| matrix ';''\n'{ANS->value.mat=$1;}
| error '\n' {yyerrok;}
;
matrix: '[' matrix_el ']' {$$=is_matrix($2);}
| MA {$$=$1->value.mat;}
| MA '=' matrix {$$=$3;$1->value.mat=$3;}
| STMA {$$=$1->value.mat;}
| matrix '+' matrix {$$=add_matrix($1,$3);}
| matrix '-' matrix {$$=minus_matrix($1,$3);}
| matrix '*' matrix {$$=multi_matrix($1,$3);}
| matrix '\'' {$$=inv_matrix($1);}
;
matrix_el: exp {$$=init_matrix($1);}
| matrix_el ',' exp {$$=wide_add_matrix($3,$1);}
| matrix_el ';' exp {$$=leng_add_matrix($3,$1);}
;
exp:   NUM {$$=$1;}
| VAR {$$=$1->value.var;}
| VAR '=' exp { $$=$3; $1->value.var=$3;}
| FNCT '(' exp ')' {$$=(*($1->value.fnctptr))($3);}
| FNCT2 '(' exp ',' exp ')' {$$=(*($1->value.fnctptr))($3,$5);}
| exp '+' exp {$$=$1+$3;}
| exp '-' exp {$$=$1-$3;}
| exp '*' exp {$$=$1*$3;}
| exp '/' exp {$$=$1/$3;}
| '-' exp %prec NEG { $$=-$2;}
| exp '^' exp {$$=pow($1,$3);}
| '(' exp ')' {$$=$2}
| CONST {$$=$1->value.var;}
%%
#include <stdio.h>
main(){
  init_table();
  yyparse();
}
yyerror(s) /* エラーがあると yyparse から呼び出される */
     char *s;
{
  printf("%s\n",s);
}
struct init{
  char *fname;
  double (*fnct)();
};

struct init arith_fncts[]
={
"sin",sin,
"cos",cos,
"atan",atan,
"ln",log,
"exp",exp,
"sqrt",sqrt,
"acos",acos,
"asin",asin,
"cosh",cosh,
"sinh",sinh,
"tanh",tanh,
"log",log10,
"ceil",ceil,
"fabs",fabs,
"floor",floor,
0,0
};
struct init arith_functs2[]
={
"atan2",atan2,
"fmod",fmod,
0,0
};
struct init_const{
char *vname;
double value;
};
struct init_const arith_const[]
={
"pi",3.14,
0,0
};
/* 記号表 : `struct symrec' のリスト */
symrec *sym_table=(symrec *)0;
init_table(){ /* 数学関数を表にする */
int i;
symrec *ptr;
ans=putsym("ans",CONST);
ANS=putsym("ANS",STMA);
for(i=0;arith_fncts[i].fname!=0;i++){
  ptr=putsym(arith_fncts[i].fname,FNCT);
  ptr->value.fnctptr=arith_fncts[i].fnct;

}
for(i=0;arith_functs2[i].fname!=0;i++){
  ptr=putsym(arith_functs2[i].fname,FNCT2);
  ptr->value.fnctptr=arith_functs2[i].fnct;
}
for(i=0;arith_const[i].vname!=0;i++){
  ptr=putsym(arith_const[i].vname,CONST);
  ptr->value.var=arith_const[i].value;
}
}
symrec * putsym(sym_name,sym_type)
   char *sym_name;
   int sym_type;
{
symrec *ptr;
ptr=(symrec *)malloc(sizeof(symrec));
ptr->name=(char*)malloc(strlen(sym_name)+1);
strcpy(ptr->name,sym_name);
ptr->type=sym_type;
ptr->value.var=0; /* 関数の場合にも値 0 にする */
ptr->next=(struct symrec *)sym_table;
sym_table=ptr;
return ptr;
}
symrec *getsym(sym_name)
   char *sym_name;
{
symrec *ptr;
for(ptr=sym_table;ptr!=(symrec *)0;ptr=(symrec *)ptr->next)
  if(strcmp(ptr->name,sym_name)==0)
    return ptr;
return 0;
}
matrix_element *init_matrix(double el){
matrix_element *root;
if((root=(matrix_element*)malloc(sizeof(matrix_element)))==NULL){
  printf("memory_error\n");
}
else{
  root->element=el;
  root->right=NULL;
  root->down=NULL;
}
return root;
}

matr *init_matr(matrix_element *root){
matr *r;
if((r=(matr*)malloc(sizeof(matr)))==NULL){
printf("memory_errlr\n");
}
else{
r->root=root;
r->leng=0;
r->wide=0;
}
return r;
}
matrix_element *wide_add_matrix(double el,matrix_element *root){
matrix_element *wide,*t,*s=root;
wide=init_matrix(el);
if(wide!=NULL){
t=root;
while(t->down!=NULL){
  s=t;
  t=t->down;
}
while(t->right!=NULL){
  t=t->right;
  if(s->right!=NULL){
    s=s->right;
  }
  else{
    return NULL;
  }
}
t->right=wide;
if(s->right!=NULL){
  s=s->right;
  s->down=wide;
}
else{
  return NULL;
}
return root;
}
else{
return NULL;
}
}
matrix_element *leng_add_matrix(double el,matrix_element *root){
matrix_element *leng,*t;

leng=init_matrix(el);
if(leng!=NULL){
 t=root;
 while(t->down!=NULL){
   t=t->down;
 }
 t->down=leng;
 return root;
}
else return NULL;
}
matr *is_matrix(matrix_element *root){
matr *r;
matrix_element *t,*s;
int n=0,m,l=0;
if(root==NULL){
  return NULL;
}
else{
  r=init_matr(root);
  if(r!=NULL){
    t=root;
    l++;
    while(t!=NULL){
      n++;
      t=t->right;
    }
    s=root->down;
    while(s!=NULL){
      t=s;
      l++;
      m=0;
      while(t!=NULL){
        m++;
        t=t->right;
      }
      if(n!=m) return NULL;
      s=s->down;
    }
  }
}
r->root=root;
r->wide=n;
r->leng=l;
return r;
}

void matrix_print(matr *root){
matrix_element *t,*s;
if(root==NULL){
  printf("no matrix\n");
}
else{
  s=root->root;
  do{
    t=s;
    while(t!=NULL){
      printf("\t%.10g",t->element);
      t=t->right;
    }
    printf("\n");
    s=s->down;
  }while(s!=NULL);
}
}
matr *make_matr(int w,int l){
matr *r;
int i,j;
if((r=(matr*)malloc(sizeof(matr)))==NULL){
  printf("memory_error\n");
  return NULL;
}
r->root=init_matrix(0.0);
r->wide=w;
r->leng=l;
for(i=0;i<l;i++){
  for(j=0;j<w-1;j++){
    r->root=wide_add_matrix(0.0,r->root);
  }
  if(i!=l-1) r->root=leng_add_matrix(0.0,r->root);
}
if(r->root==NULL) return NULL;
return r;
}
matr *add_matrix(matr *A,matr *B){
matr *C;
matrix_element *aw,*al,*bw,*bl,*cw,*cl;
if(A!=NULL && B!=NULL){
  if (A->wide==B->wide && A->leng==B->leng){
    aw=al=A->root;
    bw=bl=B->root;
  }

  else return NULL;
}
else{
  return NULL;
}
C=make_matr(A->wide,A->leng);
if(C==NULL){
   printf("memory_error\n");
   return NULL;
}
cw=cl=C->root;
while(al!=NULL && bl!=NULL && cl!=NULL){
  while(aw!=NULL && bw!=NULL && cw!=NULL){
    cw->element=aw->element+bw->element;
    aw=aw->right;
    bw=bw->right;
    cw=cw->right;
  }
  al=al->down;
  bl=bl->down;
  cl=cl->down;
  aw=al;
  bw=bl;
  cw=cl;
}
return C;
}
matr *minus_matrix(matr *A,matr *B){
matr *C;
matrix_element *aw,*al,*bw,*bl,*cw,*cl;
if(A!=NULL && B!=NULL){
  if(A->wide==B->wide && A->leng==B->leng){
    aw=al=A->root;
    bw=bl=B->root;
  }
  else return NULL;
}
else return NULL;
C=make_matr(A->wide,A->leng);
if(C==NULL){
  printf("memory_error\n");
  return NULL;
}
cw=cl=C->root;

while(al!=NULL && bl!=NULL && cl!=NULL){
while(aw!=NULL && bw!=NULL && cw!=NULL){
  cw->element=aw->element-bw->element;
  aw=aw->right;
  bw=bw->right;
  cw=cw->right;
}
al=al->down;
bl=bl->down;
cl=cl->down;
aw=al;
bw=bl;
cw=cl;
}
return C;
}
matr *multi_matrix(matr *A,matr *B){
matr *C;
matrix_element *aw,*al,*bw,*bl,*cw,*cl;
if(A!=NULL && B!=NULL){
if(A->wide==B->leng){
  aw=al=A->root;
  bw=bl=B->root;
}
else return NULL;
}
else return NULL;
C=make_matr(B->wide,A->leng);
if(C==NULL){
printf("memory_error\n");
return NULL;
}
cw=cl=C->root;
while(al!=NULL && cl!=NULL){
while(cw!=NULL && bw!=NULL){
  while(aw!=NULL && bl!=NULL){
    cw->element+=aw->element*bl->element;
    aw=aw->right;
    bl=bl->down;
  }
  cw=cw->right;
  aw=al;
  bl=bw=bw->right;
}
cl=cw=cl->down;
al=aw=al->down;

  bw=bl=B->root;
}
return C;
}
matr *inv_matrix(matr *A){
matr *T;
matrix_element *aw,*al,*tw,*tl;
if(A==NULL) return NULL;
T=make_matr(A->leng,A->wide);
if(T==NULL) return NULL;
aw=al=A->root;
tw=tl=T->root;
while(al!=NULL && tw!=NULL){
  while(aw!=NULL && tl!=NULL){
    tl->element=aw->element;
    aw=aw->right;
    tl=tl->down;
  }
  al=al->down;
  tw=tw->right;
  aw=al;
  tl=tw;
}
return T;
}
#include <ctype.h>
yylex(){
int c;

/* 空白を読み飛ばし, 空白以外を得る */

while((c=getchar())==' ' || c=='\t');
if(c==EOF)
  return 0;

/* 数値を読む */

if(c=='.' || isdigit(c)){
  ungetc(c,stdin);
  scanf("%lf",&yylval.val);
  return NUM;
}

/* 識別子を読む */

if(isalpha(c)){
  symrec *s;

static char *symbuf=0;
static int length=0;
int i;

/* バッファの長さの初期値は 40 文字 */

if(length==0)
length=40, symbuf=(char *)malloc(length+1);
i=0;
do{

/* あふれたのでバッファを大きくする */

if(i==length){
  length*=2;
  symbuf=(char *)realloc(symbuf,length+1);
}

/* 文字をバッファに変える */

symbuf[i++]=c;

/* 次の文字を読む */

c=getchar();
}while(c!=EOF && isalnum(c));
ungetc(c,stdin);
symbuf[i]='\0';
s=getsym(symbuf);
if(s==0){
  if(symbuf[0]>='A' && symbuf[0]<='Z'){
    s=putsym(symbuf,MA);
  }
  else{
    s=putsym(symbuf,VAR);
  }
}
yylval.tptr=s;
return s->type;
}

/* その他の文字は文字リテラルトークン */

return c;
}

3.2 calc.h

/* MATRIX の要素 */
struct matrix_element{
double element;
struct matrix_element *right,*down;
};
typedef struct matrix_element matrix_element;

/* MATRIX */
struct matr{
  matrix_element *root;
  int wide,leng;
};
typedef struct matr matr;

/* 記号表のリンクを表すデータ型 */

struct symrec{
  char *name; /* 記号の名前 */
  int type; /* 記号の種類:VAR,CONST または FNCT,FNCT2 */
  union{
    double var; /* VAR,CONST の値 */
    double (*fnctptr)(); /* FNCT,FNCT2 の値 */
    matr *mat; /* MATRIX */
  }value;
  struct symrec *next;
};
typedef struct symrec symrec;
/* `struct symrec' のリンクである記号表 */
extern symrec *sym_table;
symrec *putsym();
symrec *getsym();
symrec *ans;
symrec *ANS;
matrix_element *init_matrix(double);
matrix_element *wide_add_matrix(double,matrix_element*);
matrix_element *leng_add_matrix(double,matrix_element*);
void matrix_print(matr*);
matr *is_matrix(matrix_element*);
matr *add_matrix(matr*,matr*);
matr *minus_matrix(matr*,matr*);
matr *multi_matrix(matr*,matr*);
matr *make_matr(int,int);
matr *inv_matrix(matr*);