Download : sample-001.c
/*
* 2018/12/07 sample-001.c
*/
/*
* 複素数型の定義と計算
*
* 利用方法
* コンパイル
* cc -c sample-001.c
* リンク
* cc -o sample-001.exe sample-001.c
* 実行
* ./sample-001.exe
*/
#include <stdio.h>
/*
* 複素数型を表す Complex の定義
*
* 複素数 z は、二つの実数 x, y を用いて
* z = x + yi ( i は虚数単位 )
* と表現できる。
* C 言語では実数を表すのに浮動小数点数型の double を用いる
* 型名 ( Complex ) を大文字で始めるのは「ソフトウェア概論ルール」
*/
typedef struct {
double real; /* 実部 */
double imaginary; /* 虚部 */
} Complex; /* 複素数型 */
/*
* Complex make_Complex ( double x, double y )
* Complex 型の数を作り、返す
* x, y -> z = x + yi
*/
Complex make_Complex ( double x, double y ) {
Complex newComplex; /* 新しく作られる複素数 */
newComplex.real = x; /* 新しく作られた複素数の実部は x */
newComplex.imaginary = y; /* 新しく作られた複素数の実部は y */
return newComplex; /* 新しく作られる複素数を値として返す */
}
/*
* double real_part ( Complex z )
* Complex 型の数の実部を返す
* z = x + yi -> x
*/
double real_part ( Complex z ) {
return z.real;
}
/*
* double imaginary_part ( Complex z )
* Complex 型の数の実部を返す
* z = x + yi -> x
*/
double imaginary_part ( Complex z ) {
return z.imaginary;
}
/*
* print_Complex ( Complex z )
* Complex 型の数の出力
* z = x + y i だが、y < 0 の時は z = x - (-y) i となるように工夫
*/
void print_Complex ( Complex z ) {
if ( z.imaginary > 0 ) {
printf ( "%f + %f i", z.real, z.imaginary );
} else {
printf ( "%f - %f i", z.real, - z.imaginary );
}
}
/*
* Complex add_Complex ( Complex z1, Complex z2 )
* Complex 型の数の足し算
* z1 = x1 + y1 i
* z2 = x2 + y2 i
* -> z1 + z2 = ( x1 + x2 ) + ( y1 + y2 ) i
*/
Complex add_Complex ( Complex z1, Complex z2 ) {
Complex result;
/* 複素数の和の実部は、実部の和 */
result.real = z1.real + z2.real;
/* 複素数の和の虚部は、虚部の和 */
result.imaginary = z1.imaginary + z2.imaginary;
return result;
}
/*
* main
*/
int main( int argc, char *argv[] )
{
Complex z1 = make_Complex ( 2.0, 3.0 ); /* z1 = 2 + 3i */
Complex z2 = make_Complex ( -1.0, 5.0 ); /* z2 = -1 + 5i */
Complex z3;
printf ( " z1 ( = " );
print_Complex ( z1 );
printf ( " ) と、" );
printf ( " z2 ( = " );
print_Complex ( z2 );
printf ( " ) の和は 、\n" );
z3 = add_Complex ( z1, z2 ); /* z3 <- z1 + z2 */
print_Complex ( z3 );
printf ( " です。\n" );
return 0;
}
$ ./sample-001.exe z1 ( = 2.000000 + 3.000000 i ) と、 z2 ( = -1.000000 + 5.000000 i ) の和は 、 1.000000 + 8.000000 i です。 $
Download : sample-002.c
/*
* 2018/12/07 sample-002.c
*/
/*
* 二次元行列型の定義と計算
*
* 利用方法
* コンパイル
* cc -c sample-002.c
* リンク
* cc -o sample-002.exe sample-002.c
* 実行
* ./sample-002.exe
*/
#include <stdio.h>
/*
*
*/
#define DIMENSION 2 /* 二次元 */
/*
* 行列 A は、2 x 2 = 4 の要素をもっている
*
* A = ( 1 2 ) = ( a[0][0] a[0][1] )
* 3 4 a[1][0] a[1][1]
*
*/
typedef struct {
double a[DIMENSION][DIMENSION]; /* 二次元の行列の要素は 2 x 2 */
} Matrix2D; /* Matrix2D 型の宣言 */
/*
* Matrix2D make_Matrix2D ( double a, double b, double c, double d )
* 「行列」を作成する
*
* A = ( a b ) = ( a[0][0], a[0][1] )
* ( c d ) ( a[1][0], a[1][1] )
*/
Matrix2D make_Matrix2D ( double a, double b, double c, double d ) {
Matrix2D newMatrix2D; /* 新しい行列 */
newMatrix2D.a[0][0] = a;
newMatrix2D.a[0][1] = b;
newMatrix2D.a[1][0] = c;
newMatrix2D.a[1][1] = d;
return newMatrix2D;
}
/*
* void print_Matrix2D ( Matrix2D ary );
* 「行列」を表示する (表示の都合上、常に独立した行に出力する)
* Matrix2D ary; 二次元行列
*/
void print_Matrix2D ( Matrix2D ary ) {
int r; /* 行 ( row ) */
int c; /* 列 ( colomun ) */
for ( r = 0; r < DIMENSION; r++ ) {
printf ( "(" );
for ( c = 0; c < DIMENSION; c++ ) {
printf ( " %10.5f", ary.a[r][c] );
/*
* [注意] %10.5f は %f と同じく浮動小数点数を出力するが
* 「全体の桁数は 10 桁、小数点数以下は 5 桁にする」
* という「表示上の指定」も加わっている
* 詳しくは google で「printf 書式」で検索
*/
}
printf ( " )\n" );
}
}
/*
* Matrix2D add_Matrix2D ( Matrix2D a1, Matrix2D a2 );
* 「行列」の和
*
* ( a b ) + ( e f ) = ( a + e b + f )
* ( c d ) ( g h ) ( c + g g + h )
*/
Matrix2D add_Matrix2D ( Matrix2D a1, Matrix2D a2 ) {
Matrix2D result; /* 計算結果 */
int r; /* 行 ( row ) */
int c; /* 列 ( colomun ) */
for ( r = 0; r < DIMENSION; r++ ) {
for ( c = 0; c < DIMENSION; c++ ) {
result.a[r][c] = a1.a[r][c] + a2.a[r][c];
}
}
return result;
}
/*
* main
*/
int main( int argc, char *argv[] )
{
/*
a1 = ( 1 2 )
( 3 -1 )
a2 = ( -3 1 )
( 1 -2 )
*/
Matrix2D a1 = make_Matrix2D ( 1.0, 2.0, 3.0, -1.0 );
Matrix2D a2 = make_Matrix2D ( -3.0, 1.0, 1.0, -2.0 );
/* 行列 a1 と行列 a2 の和を計算して出力する */
print_Matrix2D ( a1 );
printf ( " と、 \n" );
print_Matrix2D ( a2 );
printf ( " との、和は \n" );
print_Matrix2D ( a );
printf ( " です。\n" );
return 0;
}
$ ./sample-002.exe z1 ( = 2.000000 + 3.000000 i ) と、 z2 ( = -1.000000 + 5.000000 i ) の和は 、 1.000000 + 8.000000 i です。 $
/*
* 課題 20181130-01
*
* 20181130 20181130-01-QQQQ.c
*
* 極座標で表現されている点 Q から、それと原点に対して対称な点 R を求める
*/
/*
コーディングの例
二次元平面上の点の表現方法(コーディングルール)は、一つではない
例 : 直交座標系もあるが、極座標という手もあり、その他にもいろいろある
極座標の例を試している
表現方法(コーディングルール)の違いによって、(当然)機能の実現
方法が変わるだけでなく、実現の難易度(コード量や速度だけでなく、わかりやすさも違う)も変わる
=> よいコーディングを選択する必要がある理由
*/
#include <stdio.h>
#include <math.h> /* sin, cos を利用するので.. */
/*
* void print_polar ( char name, double r, double a )
* 極座標の表示
* char name; 点の名前
* double r; 極座標の動径
* double a; 極座標の偏角
*/
void print_polar ( char name, double r, double a ) {
printf ( "点 %c の極座標は (%f,%f) です。\n", name, r, a );
}
/*
* main
*/
int main( int argc, char *argv[] )
{
/*
点 Q : 原点から 7 離れており、角度は x 軸に対して 60 度 ( Pi/3 )
*/
double Q_polar_radius = 7.0; /* 点 Q の極座標系の動径 */
double Q_polar_argument = M_PI/3; /* 点 Q の極座標系の偏角 */
double R_polar_radius; /* 点 Q と原点対称な点 R の動径 */
double R_polar_argument; /* 点 Q と原点対称な点 R の偏角 */
/*
* 点 Q の表示
*/
print_polar ( 'Q', Q_polar_radius, Q_polar_argument );
/*
* 点 R の計算
*/
/* 対称なので原点から距離は同じ */
R_polar_radius = Q_polar_radius;
/* 回転なので、原点からの距離(radius)は変化しない */
/*
** この部分を完成させなさい
*/
/* 180(π)だけ回転 */
R_polar_argument = Q_polar_argument + M_PI;
/* ラジアンでπ(円周率)の大きさは C 言語では math.h で
定義された M_PI を使って表現できる */
/* 本当は、偏角の値が、[0,2π) や [-π,π) の間などに
制限する(正規化)があった方がよいが、今回はさぼる .. */
/*
* 点 R の表示
*/
print_polar ( 'R', R_polar_radius, R_polar_argument );
return 0;
}
/*
* 課題 20181130-02
*
* 20181130 20181130-02-QQQQ.c
*
* 構造体を利用し、平行移動を行う関数を作成する
*/
#include <stdio.h>
/*
* 最初に、直交座標で「点」を表現する型 (Orthogonal) を作ってしまう
* Orthogonal 型は、二つの要素 ( x, y ) からなり、それらの型は double 型
*
* Orthogonal <----> double * double
* \in \in
* p <----> ( p.x, p.y )
*
* 残念ながら、C 言語の型定義機能で出来るのは「形(式)」の定義だけで
* 「意味」の定義はできない
* 「形」に「意味」をつけるのは、「それを扱うプログラム(関数)」の役目
*
* コーディングルール:
* 現実の世界 コンピュータの世界
*
* 平面上の点 P : ( x, y ) Orthogonal 型の pt : ( pt.x, pt.y )
* P の x 座標 : 3 pt.x = 3.0
* P の y 座標 : -2 pt.y = -2.0
*
* [注意]
* Orthogonal 型の pt を「現実の点 P」に対応させ、
* pt.x を点数 P の直交座標系における x 座標
* pt.y を点数 P の直交座標系における y 座標
* とする対応は、「决め(る)事」であり、
* 「必然的に『決る物』」では *ない*
* <反例 1>
* x と y の名前は恣意的な物なので、逆にしても問題はない
* つまり、
* pt.x を点数 P の直交座標系における y 座標
* pt.y を点数 P の直交座標系における x 座標
* と、対応させても、「プログラム上」はなんら問題ない
* (正く動くように作る事ができる)
* <反例 2>
* x と y の値の対応も恣意的な物なので、変更してもよい
* つまり、
* pt.x を点数 P の偏角
* pt.y を点数 P の動径
* 対応させても、「プログラム上」はなんら問題ない
* (正く動くように作る事ができる)
*/
typedef struct /* orthogonal */ { /* typedef で型を作ったのでタグ名不要 */
double x; /* 直交座標の x 座標を表すタグ名(x)とその型(double)の宣言 */
double y; /* 直交座標の y 座標を表すタグ名(y)とその型(double)の宣言 */
} Orthogonal; /* Orthogonal 型の宣言 */
/*
* void print_point ( Orthogonal pt );
* 「点」を表示する
* Orthogonal pt; 直交座標系の座標で表現された「点」
*/
void print_point ( Orthogonal pt ) {
/*
* 構造体の要素は、タグ名を利用して参照できる
*/
/*
printf ( "( %f, %f )", pt.x, pt.y );
*/
printf ( "( " );
printf ( "%f", pt.x ); /* double x */
printf ( ", " );
printf ( "%f", pt.y ); /* double y */
printf ( " )" );
}
/*
* Orthogonal shift_point ( Orthogonal pt, double delta_x, double delta_y )
* 点を平行移動する
* Orthogonal pt; 直交座標系の座標で表現された「点」
* double delta_x; x 軸方向の変異 (Δx)
* double delta_y; y 軸方向の変異 (Δy)
* 値 平行移動した結果
*/
Orthogonal shift_point ( Orthogonal pt, double delta_x, double delta_y ) {
Orthogonal result; /* 返す値を入れる変数 */
/* x 軸方向に delta_x だけ平行移動した result.x を得るには、
pt の x 座標に delta_x を加えればよい */
result.x = pt.x + delta_x;
/* y 軸方向に delta_y だけ平行移動した result.x を得るには、
pt の y 座標に delta_y を加えればよい */
/*
** この部分を完成させなさい
*/
return result; /* 構造体の値が返せる */
}
/*
* main
*/
int main( int argc, char *argv[] )
{
Orthogonal p1;
Orthogonal p2;
double dx = 10.0;
double dy = -100.0;
p1.x = 1.0; /* p1 = ( 1.0, 2.0 ) */
p1.y = 2.0;
/* 平行移動 */
printf ( "点 " );
/* 構造体は引数で、そのまま渡せる */
print_point ( p1 );
printf ( " を x 軸方向に %f, y 軸方向に %f 移動した点は ", dx, dy );
/* 構造体は、値としても取り出せるし、普通に代入もできる */
p2 = shift_point ( p1, dx, dy );
/*
p2 = p1; 代入も可能
p2.x = p1.x
p2.y = p2.y
と同じ意味(同じ結果)になる
*/
print_point ( p2 );
printf ( " となります。\n" );
return 0;
}
#include <stdio.h>
#include <math.h> /* 関数 sqrt を利用するので... */
/*
平面上の「点」を(C 言語内で..)表現したい
=> 典型的なアプローチとして、直交座標系を導入して、
その座標系における、x,y 座標の「組」で「点」を表現
*/
int main(int argc, char *argv[]) {
double p1x, p1y; /* 点 P1を表現する変数 p1x と p1y */
double p2x, p2y; /* 点 P2を表現する変数 p2x と p2y */
/* p1x と p1y、p2x と p2y は、それぞれペアになって、一緒に動く */
p1x = 1.0; p1y = 2.0; /* P1 <- (1.0,2.0) */
p2x = -2.0; p2y = 3.0; /* P2 <- (-2.0,3.0) */
printf ( "P1(%f,%f) と P2(%f,%f) の距離は %f です\n",
p1x, p1y, p2x, p2y,
sqrt ( (p2x-p1x)*(p2x-p1x) + (p2y-p1y)*(p2y-p1y) ) );
return 0;
}
#include <stdio.h>
#include <math.h>
int main(int argc, char *argv[]) {
double v1, v2;
double v3, v4;
v1 = 1.0; v2 = 2.0;
v3 = -2.0; v4 = 3.0;
printf ( "P1(%f,%f) と P2(%f,%f) の距離は %f です\n",
v1, v2, v3, v4,
sqrt ( (v3-v1)*(v3-v1) + (v4-v2)*(v4-v2) ) );
return 0;
}
#include <stdio.h>
#include <math.h> /* 関数 sqrt を利用するので... */
/*
平面上の「点」を(C 言語内で..)表現したい
=> 典型的なアプローチとして、直交座標系を導入して、
その座標系における、x,y 座標の「組」で「点」を表現
*/
/*
目的 : 点を直交座標で表現するデータ型を作りたい
[構造体の定義方法]
1. 構成要素を列挙する
=> x, y
2. 個々の構成要素の型を考える
=> 浮動小数点数型(double)
3, 対の名前(構造体名[タグ])を決める
=> point
4. 名前の前に struct をかき、先の二つ要素を {} でかこみ、最後に ;
[構造体の利用方法]
1. 「struct タグ名」が、「型名」としてふるまうので、
その型の変数宣言をする
=>
struct point p1;
struct point p2;
2. もし、その一部(メンバー)を参照したければ、
「.」と、メンバー名で、その値を参照できる
3. 個々のメンバーは、独立した変数してふるまう
*/
struct point {
double x; /* 点の x 座標 */
double y; /* 点の y 座標 */
};
/* 構造体 point の定義がされた */
int main(int argc, char *argv[]) {
struct point p1; /* 点 P1を表現する変数 */
struct point p2; /* 点 P2を表現する変数 */
p1.x = 1.0; p1.y = 2.0; /* P1 <- (1.0,2.0) */
p2.x = -2.0; p2.y = 3.0; /* P2 <- (-2.0,3.0) */
printf ( "P1(%f,%f) と P2(%f,%f) の距離は %f です\n",
p1.x, p1.y, p2.x, p2.y,
sqrt ( (p2.x-p1.x)*(p2.x-p1.x) + (p2.y-p1.y)*(p2.y-p1.y) ) );
return 0;
}
#include <stdio.h>
#include <math.h> /* 関数 sqrt を利用するので... */
/*
構造体を定義する事により、新しい型が作られる
=> 「stuct point」は、「int」 や 「double」 の代わりに使える
ただし、毎回 stuct を構造体タグ名の前にかく必要がある
typedef : 型に名前を付ける(新しい型の定義)の機能がある
構造体の定義の前に typedef
後ろのセミコロンの前に、新しい型名をいれればよい。
=> その型名の型が作られる
[注意] 構造体を扱う上で、typedef は必ずしも必要でないが、
基本、構造体は typedef で、型定義をするためだけに利用する
*/
typedef struct point {
double x; /* 点の x 座標 */
double y; /* 点の y 座標 */
} Point;
/* 構造体 point と同じ構造を持つ Point という型が作られた */
int main(int argc, char *argv[]) {
Point p1; /* 点 P1を表現する変数 */
Point p2; /* 点 P2を表現する変数 */
p1.x = 1.0; p1.y = 2.0; /* P1 <- (1.0,2.0) */
p2.x = -2.0; p2.y = 3.0; /* P2 <- (-2.0,3.0) */
printf ( "P1(%f,%f) と P2(%f,%f) の距離は %f です\n",
p1.x, p1.y, p2.x, p2.y,
sqrt ( (p2.x-p1.x)*(p2.x-p1.x) + (p2.y-p1.y)*(p2.y-p1.y) ) );
return 0;
}
#include <stdio.h>
#include <math.h> /* 関数 sqrt を利用するので... */
/*
typedef を利用して、新しい型 Point が作られた
「型」: その型の要素となる集合とその集合上の演算の対
! C 言語では、新しい型を作っても、その型上の演算は、ほとんどない
! => 構成要素を参照する「.」はあるが..(構造体の機能/typedefの機能ではない)
*/
typedef struct point {
double x; /* 点の x 座標 */
double y; /* 点の y 座標 */
} Point;
/* 構造体 point と同じ構造を持つ Point という型が作られた */
/*
以下、新しい型 Point に対し、その型上演算(関数)を定義する
/*
二つの点が与えられたときに、その二点間の距離を求める
*/
double distance ( Point p1, Point p2 ) {
return sqrt ( (p2.x-p1.x)*(p2.x-p1.x) + (p2.y-p1.y)*(p2.y-p1.y) );
}
void printPoint ( char *name, Point p ) {
printf ( "%s (%f,%f)", name, p.x, p.y );
}
Point initPoint ( double ix, double iy ) {
Point result; /* 与えらえた、x, y 座標を持つ点を作って返す */
result.x = ix;
result.y = iy;
return result;
}
/*
main
*/
int main(int argc, char *argv[]) {
Point p1; /* 点 P1を表現する変数 */
Point p2; /* 点 P2を表現する変数 */
Point p3;
p1 = initPoint( 1.0, 2.0 ); /* P1 <- (1.0,2.0) */
p2 = initPoint( -2.0, 3.0 ); /* P2 <- (-2.0,3.0) */
p3 = initPoint( 4.0, 0.0) ; /* P3 <- (4.0,0.0) */
printPoint ( "P1", p1 );
printf ( " と " );
printPoint ( "P2", p2 );
printf ( " の距離は %f です\n", distance( p1, p2 ) );
printPoint ( "P1", p1 );
printf ( " と " );
printPoint ( "P3", p3 );
printf ( " の距離は %f です\n", distance( p1, p3 ) );
return 0;
}
#include <stdio.h>
/*
空間の点
=> (x,y,z) の三つの直交座標で
しかし、一般的に n 次元になったら.. ?
*/
typedef struct pint3D {
double x; /* 同じデータの複数の組み合わせ */
double y;
double z;
} Point3D;
/*
原点に対して、点対称な点を求める
*/
Point3D rot( Point3D pt ) {
Point3D result;
result.x = -pt.x; /* 同じ事を繰り返している */
result.y = -pt.y;
result.z = -pt.z;
return result;
}
int main(int argc, char *argv[]) {
Point3D p;
Point3D r;
p.x = -1.0;
p.y = 2.0;
p.z = 4.0;
r = rot( p );
printf ( "Q(%f,%f,%f)\n", r.x, r.y, r.z );
return 0;
}
#include <stdio.h>
int main(int argc, char *argv[]) {
int v0;
int v1;
int v2;
v0 = 0;
v1 = 1;
v2 = 2;
v0 = v0 * v0;
v1 = v1 * v1;
v2 = v2 * v2;
printf ( "%d\n", v0 );
printf ( "%d\n", v1 );
printf ( "%d\n", v2 );
}
#include <stdio.h>
int main(int argc, char *argv[]) {
/*
int v0;
int v1;
int v2;
*/
int v[3]; /* これは、実質 int v[0], int v[1], int v[2] と書いたのと同じ */
v[0] = 0;
v[1] = 1;
v[2] = 2;
v[0] = v[0] * v[0];
v[1] = v[1] * v[1];
v[2] = v[2] * v[2];
printf ( "%d\n", v[0] );
printf ( "%d\n", v[1] );
printf ( "%d\n", v[2] );
}
#include <stdio.h>
int main(int argc, char *argv[]) {
/*
int v0;
int v1;
int v2;
*/
int v[3]; /* 要素が 3 つの数列 { v[0], v[1], v[2] } を表現している */
/* 数列は、添え字を利用して、その要素が参照できる */
int i; /* インデックス:添え字 */
i = 0;
v[i] = 0; /* v[i] -> (i=0なので..) v[0] と同じ */
i = 1;
v[i] = 1;
i = 2;
v[i] = 2;
/* 以下省略 */
v[0] = v[0] * v[0];
v[1] = v[1] * v[1];
v[2] = v[2] * v[2];
printf ( "%d\n", v[0] );
printf ( "%d\n", v[1] );
printf ( "%d\n", v[2] );
}
#include <stdio.h>
int main(int argc, char *argv[]) {
/*
int v0;
int v1;
int v2;
*/
int v[3]; /* 要素が 3 つの数列 { v[0], v[1], v[2] } を表現している */
/* 数列は、添え字を利用して、その要素が参照できる */
int i; /* インデックス:添え字 */
i = 0;
v[i] = i; /* v[i] -> (i=0なので..) v[0] と同じ */
i = 1;
v[i] = i;
i = 2;
v[i] = i;
/* 以下省略 */
v[0] = v[0] * v[0];
v[1] = v[1] * v[1];
v[2] = v[2] * v[2];
printf ( "%d\n", v[0] );
printf ( "%d\n", v[1] );
printf ( "%d\n", v[2] );
}
#include <stdio.h>
#define SIZE 3
int main(int argc, char *argv[]) {
/*
int v0;
int v1;
int v2;
*/
int v[SIZE]; /* 要素が 3 つの数列 { v[0], v[1], v[2] } を表現している */
/* 数列は、添え字を利用して、その要素が参照できる */
int i; /* インデックス:添え字 */
for ( i = 0; i < SIZE; i++ ) {
v[i] = i;
}
for ( i = 0; i < SIZE; i++ ) {
v[i] = v[i]*v[i];
}
for ( i = 0; i < SIZE; i++ ) {
printf ( "%d\n", v[i] );
}
}
[前回の復習]
データ構造
従来はプログラム構造
プログラミング基本 : (C 言語のような手続き型の言語による)プログラムは、
「命令の列を作る」という作業
=> プログラム構造(既存から新しい機能を作り上げる仕組み)が書ければ OK
# プログラムは人間が作る
# そして、人間は間違える生き物
# できる(数学/理学)だけでだめで、やりやすい(情報/工学)の発想が必要
# => プログラムをより書きやすい(人間視点で..)方法を説明
!! 時代 : 手続きから => データ => オブジェクト
なぜデータ構造
1. (論理値=>データで、コードが制御できる) データの表現で、プログラムの機能が実現できる(万能性の根拠)
=> データの方が、プログラムより柔軟なので、データを中心とした考え方が必要
2. プログラムが現実と対応づけできる(プログラムが役立つ)ためには、現実と(プログラム内の)データが対応付けされないといけない
=> コーディングが不可欠
=> コーディングの良し悪しが、プログラム作成の難易度に影響する
# *よい* コーディングは、同じ機能を実現するときに、プログラムが簡単に作れる
[今日の内容]
(従来の学習内容で..) 「点」を表す場合に、直交座標というコーディング規則を利用し、
x 座標と y 座標の組で、表す事にする
二つの変数の組で、一つの対象(点)を表現するが、
その一つの対象を表現する二つの変数の関係が、プログラマの頭の中にしかない..
=> 「分からなく」なってしまう(プログラマの思考内容は、記録されていないので、場合によっては失われてしまう..)
# 必要(プログラムの機能のためでなく、プログラムを理解するため)情報は、(プログラムの機能を実現するために、本質的に不要でも..)記述しておく必要がある
# => プログラムをわかりやすくするために重要(プログラムを作成しやすくするので..)
データ(変数)間の関係(同じ点を表現するために、ついになっているx座標を表す変数と、y 座標を表す変数)を明示的に表現する記述方法が欲しい
=> 構造体
構造体の導入の効果
複数の型のデータの組み合わせから作られる新しい型が作れる
=> 複雑なデータの操作を、簡単な表現で行える(例:代入)
ただし、C 言語では、あたしい型のデータに対する、基本的な操作は、(自動的には)提供されない
cf. そのデータ型の値をどうやって入出力するか .. ?
新しい型と元の(材料となった)型の関係を利用した操作は可能なので、
全体の操作を、材料の操作で実現する必要がある
基本的操作は、(ほぼ、機械的に..) データ構造に対応した形でプログラムを作成すれば、実現可能
cf. 代入/入出力/全体的な操作/etc..
=> データ構造が、プログラム構造を決定する(という傾向がある..)
配列
[0]
int v0;
int v1;
int v2;
v0 = 0:
v1 = 1:
v2 = 2:
[1]
int v[3];
v[0] = 0:
v[1] = 1:
v[2] = 2:
[2]
int v[3];
int i;
i=0;
v[i] = 0:
i=1;
v[i] = 1:
i=2;
v[i] = 2:
[2]
int v[3];
int i;
i=0;
v[i] = i:
i=1;
v[i] = i:
i=2;
v[i] = i:
[3]
int v[3];
int i;
i=0;
v[i] = i:
i=i+1;
v[i] = i:
i=i+1;
v[i] = i:
[4]
int v[3];
int i;
i=0;
v[i] = i:
i=i+1;
v[i] = i:
i=i+1;
v[i] = i:
i=i+1;
[4]
int v[3];
int i;
for (i=0;i<3;i=i+1) {
v[i] = i:
}
課題プログラム内の「/*名前:ここ*/」の部分を書き換え「/*この部分を完成させなさい*/」の部分にプログラムを追加して、プログラムを完成させます。
Download : 20181207-01.c
/*
* 課題 20181207-01
*
* 20181207 20181207-01-QQQQ.c
*
* 複素数型の四則
*/
#include <stdio.h>
/*
* 複素数型の定義と計算
*
* 利用方法
* コンパイル
* cc -Ic:\usr\c\include -o BASENAME.exe 20181207-01-QQQQ.c
* 実行
* BASENAME
*/
#include <stdio.h>
/*
* 複素数型を表す Complex の定義
*
* 複素数 z は、二つの実数 x, y を用いて
* z = x + yi ( i は虚数単位 )
* と表現できる。
* C 言語では実数を表すのに浮動小数点数型の double を用いる
* 型名 ( Complex ) を大文字で始めるのは「ソフトウェア概論ルール」
*/
typedef struct {
double real; /* 実部 */
double imaginary; /* 虚部 */
} Complex; /* 複素数型 */
/*
* Complex make_Complex ( double x, double y )
* Complex 型の数を作り、返す
* x, y -> z = x + yi
*/
Complex make_Complex ( double x, double y ) {
Complex newComplex; /* 新しく作られる複素数 */
newComplex.real = x; /* 新しく作られた複素数の実部は x */
newComplex.imaginary = y; /* 新しく作られた複素数の実部は y */
return newComplex; /* 新しく作られる複素数を値として返す */
}
/*
* double real_part ( Complex z )
* Complex 型の数の実部を返す
* z = x + yi -> x
*/
double real_part ( Complex z ) {
return z.real;
}
/*
* double imaginary_part ( Complex z )
* Complex 型の数の実部を返す
* z = x + yi -> x
*/
double imaginary_part ( Complex z ) {
return z.imaginary;
}
/*
* print_Complex ( Complex z )
* Complex 型の数の出力
* z = x + y i だが、y < 0 の時は z = x - (-y) i となるように工夫
*/
void print_Complex ( Complex z ) {
if ( z.imaginary > 0.0 ) {
printf ( "%f + %f i", z.real, z.imaginary );
} else {
printf ( "%f - %f i", z.real, - z.imaginary );
}
}
/*
* Complex add_Complex ( Complex z1, Complex z2 )
* Complex 型の数の足し算
* z1 = x1 + y1 i
* z2 = x2 + y2 i
* -> z1 + z2 = ( x1 + x2 ) + ( y1 + y2 ) i
*/
Complex add_Complex ( Complex z1, Complex z2 ) {
Complex result;
/* 複素数の和の実部は、実部の和 */
result.real = z1.real + z2.real;
/* 複素数の和の虚部は、虚部の和 */
result.imaginary = z1.imaginary + z2.imaginary;
return result;
}
/*
* Complex sub_Complex ( Complex z1, Complex z2 )
* Complex 型の数の引き算
* z1 = x1 + y1 i
* z2 = x2 + y2 i
* -> z1 - z2 = ( x1 - x2 ) + ( y1 - y2 ) i
*/
Complex sub_Complex ( Complex z1, Complex z2 ) {
Complex result;
/* 複素数の差の実部は、実部の差 */
result.real = z1.real - z2.real;
/* 複素数の差の虚部は、虚部の差 */
/*
** この部分を完成させなさい
*/
return result;
}
/*
* Complex mul_Complex ( Complex z1, Complex z2 )
* Complex 型の数のかけ算
* z1 = x1 + y1 i
* z2 = x2 + y2 i
* の時
* z1 * z2 = (x1 * x2 - y1 * y2) + (x1 * y2 + x2 * y1) i
*/
Complex mul_Complex ( Complex z1, Complex z2 ) {
Complex result;
result.real = z1.real * z2.real - z1.imaginary * z2.imaginary;
/*
** この部分を完成させなさい
*/
return result;
}
/*
* Complex div_Complex ( Complex z1, Complex z2 )
* Complex 型の数の割り算
* z1 = x1 + y1 i
* z2 = x2 + y2 i
* の時
* z1 / z2 = ( x1 + y1 i ) / ( x2 + y2 i )
* = ( x1 + y1 i )( x2 - y2 i ) / ( x2 + y2 i )( x2 - y2 i )
* = ( x1 * x2 + y1 * y2) / ( x2^2 + y2^2 )
* + ( (- x1 * y2 + x2 * y1) / ( x2^2 + y2^2 ) ) i
*/
Complex div_Complex ( Complex z1, Complex z2 ) {
Complex result;
double denominator = z2.real * z2.real + z2.imaginary *z2.imaginary;
/* 実部、虚部の割る数 |z2|^2 を予め計算しておく */
/*
** この部分を完成させなさい
*/
result.imaginary = ( - z1.real * z2.imaginary + z1.imaginary * z2.real ) / denominator;
return result;
}
/*
* print_result 演算結果を出力する
*/
void print_result ( Complex z1, Complex z2, char *operator, Complex z ) {
print_Complex ( z1 );
printf ( " と、 " );
print_Complex ( z2 );
printf ( " との、%s は ", operator );
print_Complex ( z );
printf ( " です。\n" );
}
/*
* main
*/
int main( int argc, char *argv[] )
{
Complex z1 = make_Complex ( 20.0, -15.0 ); /* z1 = 20 - 15i */
Complex z2 = make_Complex ( 1.0, 2.0 ); /* z2 = 1 + 2i */
/* 和の出力 */
print_result ( z1, z2, "和", add_Complex ( z1, z2 ) );
/* 差の出力 */
/*
** この部分を完成させなさい
*/
/* 積の出力 */
/*
** この部分を完成させなさい
*/
/* 商の出力 */
print_result ( z1, z2, "商", div_Complex ( z1, z2 ) );
return 0;
}
$ ./20181207-01-QQQQ.exe 20.000000 - 15.000000 i と、 1.000000 + 2.000000 i との、和 は 21.000000 - 13.000000 i です。 20.000000 - 15.000000 i と、 1.000000 + 2.000000 i との、差 は 19.000000 - 17.000000 i です。 20.000000 - 15.000000 i と、 1.000000 + 2.000000 i との、積 は 50.000000 + 25.000000 i です。 20.000000 - 15.000000 i と、 1.000000 + 2.000000 i との、商 は -2.000000 - 11.000000 i です。 $
Download : 20181207-02.c
/*
* 課題 20181207-02
*
* 20181207 20181207-02-QQQQ.c
*
* 二次元行列型の定義と計算
*/
#include <stdio.h>
/*
*
*/
#define DIMENSION 2 /* 二次元 */
/*
* 行列 A は、2 x 2 = 4 の要素をもっている
*
* A = ( 1 2 ) = ( a[0][0] a[0][1] )
* 3 4 a[1][0] a[1][1]
*
*/
typedef struct {
double a[DIMENSION][DIMENSION]; /* 二次元の行列の要素は 2 x 2 */
} Matrix2D; /* Matrix2D 型の宣言 */
/*
* Matrix2D make_Matrix2D ( double a, double b, double c, double d )
* 「行列」を作成する
*
* A = ( a b ) = ( a[0][0], a[0][1] )
* ( c d ) ( a[1][0], a[1][1] )
*/
Matrix2D make_Matrix2D ( double a, double b, double c, double d ) {
Matrix2D newMatrix2D; /* 新しい行列 */
newMatrix2D.a[0][0] = a;
newMatrix2D.a[0][1] = b;
newMatrix2D.a[1][0] = c;
newMatrix2D.a[1][1] = d;
return newMatrix2D;
}
/*
* void print_Matrix2D ( Matrix2D ary );
* 「行列」を表示する (表示の都合上、常に独立した行に出力する)
* Matrix2D ary; 二次元行列
*/
void print_Matrix2D ( Matrix2D ary ) {
int r; /* 行 ( row ) */
int c; /* 列 ( colomun ) */
for ( r = 0; r < DIMENSION; r++ ) {
printf ( "(" );
for ( c = 0; c < DIMENSION; c++ ) {
printf ( " %10.5f", ary.a[r][c] );
/*
* [注意] %10.5f は %f と同じく浮動小数点数を出力するが
* 「全体の桁数は 10 桁、小数点数以下は 5 桁にする」
* という「表示上の指定」も加わっている
* 詳しくは google で「printf 書式」で検索
*/
}
printf ( " )\n" );
}
}
/*
* Matrix2D add_Matrix2D ( Matrix2D a1, Matrix2D a2 );
* 「行列」の和
*
* ( a b ) + ( e f ) = ( a + e b + f )
* ( c d ) ( g h ) ( c + g g + h )
*/
Matrix2D add_Matrix2D ( Matrix2D a1, Matrix2D a2 ) {
Matrix2D result; /* 計算結果 */
int r; /* 行 ( row ) */
int c; /* 列 ( colomun ) */
for ( r = 0; r < DIMENSION; r++ ) {
for ( c = 0; c < DIMENSION; c++ ) {
result.a[r][c] = a1.a[r][c] + a2.a[r][c];
}
}
return result;
}
/*
* Matrix2D sub_Matrix2D ( Matrix2D a1, Matrix2D a2 );
* 「行列」の差
*
* ( a b ) - ( e f ) = ( a - e b - f )
* ( c d ) ( g h ) ( c - g g - h )
*/
Matrix2D sub_Matrix2D ( Matrix2D a1, Matrix2D a2 ) {
Matrix2D result; /* 計算結果 */
int r; /* 行 ( row ) */
int c; /* 列 ( colomun ) */
for ( r = 0; r < DIMENSION; r++ ) {
for ( c = 0; c < DIMENSION; c++ ) {
/*
** この部分を完成させなさい
*/
}
}
return result;
}
/*
* Matrix2D mul_Matrix2D ( Matrix2D a1, Matrix2D a2 );
* 「行列」の積
*
* ( a b ) ( e f ) = ( a * e + b * g a * f + b * h )
* ( c d ) ( g h ) ( c * e + d * g c * f + d * h )
*/
Matrix2D mul_Matrix2D ( Matrix2D a1, Matrix2D a2 ) {
Matrix2D result; /* 計算結果 */
int r; /* 行 ( row ) */
int c; /* 列 ( colomun ) */
int i;
for ( r = 0; r < DIMENSION; r++ ) {
for ( c = 0; c < DIMENSION; c++ ) {
double products = 0.0; /* a1 の r 行と a2 の c 列の内積の結果 */
/* a1 の r 行と a2 の c 列の内積を計算する */
/*
** この部分を完成させなさい
*/
result.a[r][c] = products;
}
}
return result;
}
/*
* print_result 演算結果を出力する
*/
void print_result ( Matrix2D a1, Matrix2D a2, char *operator, Matrix2D a ) {
printf ( "%s の計算\n", operator );
print_Matrix2D ( a1 );
printf ( " と、 \n" );
print_Matrix2D ( a2 );
printf ( " との、%s は \n", operator );
print_Matrix2D ( a );
printf ( " です。\n\n" );
}
/*
* main
*/
int main( int argc, char *argv[] )
{
/*
a1 = ( 1 2 )
( 3 -1 )
a2 = ( -3 1 )
( 1 -2 )
*/
Matrix2D a1 = make_Matrix2D ( 1.0, 2.0, 3.0, -1.0 );
Matrix2D a2 = make_Matrix2D ( -3.0, 1.0, 1.0, -2.0 );
/* 和の出力 */
print_result ( a1, a2, "和", add_Matrix2D ( a1, a2 ) );
/* 差の出力 */
/*
** この部分を完成させなさい
*/
/* 積の出力 */
/*
** この部分を完成させなさい
*/
return 0;
}
123 987 456
$ ./20181207-02-QQQQ.exe 和 の計算 ( 1.00000 2.00000 ) ( 3.00000 -1.00000 ) と、 ( -3.00000 1.00000 ) ( 1.00000 -2.00000 ) との、和 は ( -2.00000 3.00000 ) ( 4.00000 -3.00000 ) です。 差 の計算 ( 1.00000 2.00000 ) ( 3.00000 -1.00000 ) と、 ( -3.00000 1.00000 ) ( 1.00000 -2.00000 ) との、差 は ( 4.00000 1.00000 ) ( 2.00000 1.00000 ) です。 積 の計算 ( 1.00000 2.00000 ) ( 3.00000 -1.00000 ) と、 ( -3.00000 1.00000 ) ( 1.00000 -2.00000 ) との、積 は ( -1.00000 -3.00000 ) ( -10.00000 5.00000 ) です。 $
Download : 20181207-03.c
/*
* 課題 20181207-03
*
* 20181207 20181207-03-QQQQ.c
*
* 整数型の配列を作り、それに 5 個のデータを入力し
* その値を 5 倍にしたものと 2 分の 1 にした値を
* それぞれ、画面に出力するプログラムを作りなさい
*
*/
#include <stdio.h>
/*
*
*/
#define DATA_SIZE 5 /* データのサイズ (個数) */
/*
*
*/
int main(int argc, char *argv[]) {
int array[DATA_SIZE]; /* サイズが DATA_SIZE の整数型の配列 array の宣言 */
int i; /* 添字変数 i を宣言 */
for ( i = 0; i < DATA_SIZE; i++ ) { /* 配列 array に数値を読み込む */
/* プロンプト */
printf ( "%d 番目の整数値を入力してください : ", i + 1 );
/* 配列へのデータ入力 */
scanf ( "%d", &array[i] );
}
/* 入力された個々の値を 5 倍した物を出力 */
for ( i = 0; i < DATA_SIZE; i++ ) {
printf ( "%d\n", /* p:ここ */ ); /* 5 倍 */
}
/* 入力された個々の値を 1/2 した物を出力 */
for ( i = 0; i < DATA_SIZE; i++ ) {
printf ( "%d\n", /* q:ここ */ ); /* 2 分の 1 */
/* 整数割り算になるので、小数点以下は切り捨て */
}
return 0;
}
3 8 13 2 4
$ ./20181207-03-QQQQ.exe $