Download : sample-001.c
/* * 2018/11/30 sample-001.c */ /* * 銀行口座への振込プログラム * * 利用方法 * コンパイル * cc -c sample-001.c * リンク * cc -o sample-001.exe sample-001.c * 実行 * ./sample-001.exe */ #include <stdio.h> /* * main * * 現実の世界 プログラムの世界 * * [表現] 栗野の口座 kurino_account * * [事前] 100 万円 kurino_account = 1000000 * * 振込額 10 万円 transfer_money = 100000 * <振込> kurino_account = kurino_account + transfer_money * [事後] 110 万円 * * <振込> という「情報上の機能」 <足し算> という「数値上の操作」 */ int main( int argc, char *argv[] ) { int kurino_account = 1000000; /* 栗野の銀行口座に 100 万円入っている */ int transfer_money = 100000; /* 10 万円の振込をしたい.. */ printf ( "現在の栗野の残高は %d 万円です。\n", kurino_account / 10000 ); /* <振込> を行うプログラム */ printf ( "栗野の口座に %d 万円の振込を行います。\n", transfer_money / 10000 ); /* 「足し算」が「振込」になる */ kurino_account = kurino_account + transfer_money; printf ( "現在の栗野の残高は %d 万円です。\n", kurino_account / 10000 ); return 0; }
$ ./sample-001.exe 現在の栗野の残高は 100 万円です。 栗野の口座に 10 万円の振込を行います。 現在の栗野の残高は 110 万円です。 $
Download : sample-002.c
/* * 2018/11/30 sample-002.c */ /* * ASCII Code を利用した「文字」の操作 * * 利用方法 * コンパイル * cc -c sample-002.c * リンク * cc -o sample-002.exe sample-002.c * 実行 * ./sample-002.exe */ #include <stdio.h> /* * main */ int main( int argc, char *argv[] ) { char mathematics_record = 'B'; /* 現在の数学の評価は 'B' */ printf ( "数学の前評価の結果は %c でした。\n", mathematics_record ); printf ( "再度確認した所、採点ミスが見付かり、加点した所、グレードが一つ高くなりました。\n" ); /* * */ /* 成績のグレードを高くするために 'B' を 'A' にする */ /* 現実の世界 データ/表現 プログラムの世界 ASICC Code ('B'=66, 'A'=65) グレードを一段階「高く」する : 'B' -----> 'A' 66 -----> 65 : 1 減らす */ mathematics_record = mathematics_record - 1; /* * */ printf ( "その結果、数学の最終評価は %c になりました。\n", mathematics_record ); return 0; }
$ ./sample-002.exe 数学の前評価の結果は B でした。 再度確認した所、採点ミスが見付かり、加点した所、グレードが一つ高くなりました。 その結果、数学の最終評価は A になりました。 $
Download : sample-003.c
/* * 2018/11/30 sample-003.c */ /* * 平面上の「点」の二つの表現銀行口座への振込プログラム * * 利用方法 * コンパイル * cc -c sample-003.c * リンク ( M_PI,sin,cos を利用するので 「-lm」が必須 ) * cc -o sample-003.exe sample-003.c -lm * 実行 * ./sample-003.exe */ #include <stdio.h> #include <math.h> /* sin, cos を利用するので.. */ /* * void print_orthogonal ( char name, double x, double y ) * 直交座標の表示 * char name; 点の名前 * double x; 直交座標の X 座標 * double y; 直交座標の Y 座標 */ void print_orthogonal ( char name, double x, double y ) { printf ( "点 %c の直交座標は (%f,%f) です。\n", name, x, y ); } /* * 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[] ) { /* 点 P : 座標 (2,3) */ double P_orthogonal_x = 2.0; /* 点 P の直交座標系の x 座標 */ double P_orthogonal_y = 3.0; /* 点 P の直交座標系の y 座標 */ double P_polar_radius; /* 点 P の極座標系の動径 */ double P_polar_argument; /* 点 P の極座標系の偏角 */ /* 点 Q : 原点から 7 離れており、角度は x 軸に対して 60 度 ( Pi/3 ) */ double Q_orthogonal_x; /* 点 Q の直交座標系の x 座標 */ double Q_orthogonal_y; /* 点 Q の直交座標系の y 座標 */ double Q_polar_radius = 7.0; /* 点 Q の極座標系の動径 */ double Q_polar_argument = M_PI/3; /* 点 Q の極座標系の偏角 */ /* * 点 P の表示 */ print_orthogonal ( 'P', P_orthogonal_x, P_orthogonal_y ); /* * r = \sqrt{x^2+y^2} なので */ P_polar_radius = sqrt ( P_orthogonal_x * P_orthogonal_x + P_orthogonal_y * P_orthogonal_y ); /* * a = \tan^{-1}{y/x} なので * cf. http://www1.cts.ne.jp/~clab/hsample/Math/Math2.html */ P_polar_argument = atan ( P_orthogonal_y / P_orthogonal_x ); print_polar ( 'P', P_polar_radius , P_polar_argument ); /* * 点 Q の表示 */ print_polar ( 'Q', Q_polar_radius, Q_polar_argument ); /* * x = r \cos{a} なので */ Q_orthogonal_x = Q_polar_radius * cos( Q_polar_argument ); /* * y = r \sin{a} なので */ Q_orthogonal_y = Q_polar_radius * sin( Q_polar_argument ); print_orthogonal ( 'Q', Q_orthogonal_x, Q_orthogonal_y ); return 0; }
$ ./sample-003.exe 点 P の直交座標は (2.000000,3.000000) です。 点 P の極座標は (3.605551,0.982794) です。 点 Q の極座標は (7.000000,1.047198) です。 点 Q の直交座標は (3.500000,6.062178) です。 $
Download : sample-004.c
/* * 2018/11/30 sample-004.c */ /* * 直交座標で表現されている点 Q から、それと原点に対して対称な点 R を求める * * 利用方法 * コンパイル * cc -c sample-004.c * リンク * cc -o sample-004.exe sample-004.c * 実行 * ./sample-004.exe */ #include <stdio.h> /* * void print_orthogonal ( char name, double x, double y ) * 直交座標の表示 * char name; 点の名前 * double x; 直交座標の X 座標 * double y; 直交座標の Y 座標 */ void print_orthogonal ( char name, double x, double y ) { printf ( "点 %c の直交座標は (%f,%f) です。\n", name, x, y ); } /* * main */ int main( int argc, char *argv[] ) { /* 点 P : 座標 (2,3) */ double P_orthogonal_x = 2.0; /* 点 P の直交座標系の x 座標 */ double P_orthogonal_y = 3.0; /* 点 P の直交座標系の y 座標 */ double R_orthogonal_x; /* 点 P と原点対称な点 R の x 座標 */ double R_orthogonal_y; /* 点 P と原点対称な点 R の y 座標 */ /* * 点 P の表示 */ print_orthogonal ( 'P', P_orthogonal_x, P_orthogonal_y ); /* * 点 R の計算 */ /* R の x 座標は P の x 座標の符号を変えた物 */ R_orthogonal_x = - P_orthogonal_x; /* R の y 座標は P の y 座標の符号を変えた物 */ R_orthogonal_y = - P_orthogonal_y; /* * 点 R の表示 */ print_orthogonal ( 'R', R_orthogonal_x, R_orthogonal_y ); return 0; }
$ ./sample-004.exe 点 P の直交座標は (2.000000,3.000000) です。 点 R の直交座標は (-2.000000,-3.000000) です。 $
Download : sample-005.c
/* * 2018/11/30 sample-005.c */ /* * 平面上の点を扱う * * 利用方法 * コンパイル * cc -c sample-005.c * リンク * cc -o sample-005.exe sample-005.c -lm * 実行 * ./sample-005.exe */ #include <stdio.h> #include <math.h> /* sqrt を利用するので必要 (-lm も忘れずに ) */ /* * void print_point ( double px, double py ) * 「点」を表示する * double px -- 「点」の x 座標 * double py -- 「点」の y 座標 */ void print_point ( double px, double py ) { printf ( "( %f, %f )", px, py ); } /* * double point_distance ( double p1x, double p1y, double p2x, double p2y ) * ニ「点」間の距離を返す * double p1x -- 「始点」の x 座標 * double p1y -- 「始点」の y 座標 * double p2x -- 「終点」の x 座標 * double p2y -- 「終点」の y 座標 */ double point_distance ( double p1x, double p1y, double p2x, double p2y ) { double dx = p2x - p1x; /* x 座標の差 */ double dy = p2y - p1y; /* y 座標の差 */ return sqrt ( dx*dx + dy*dy ); } /* * main */ int main( int argc, char *argv[] ) { double p1x = 1.0; /* p1 = ( 1.0, 2.0 ) */ double p1y = 2.0; double p2x = 4.0; /* p2 = ( 4.0, 6.0 ) */ double p2y = 6.0; printf ( "始点 " ); print_point ( p1x, p1y ); printf ( " と終点 " ); print_point ( p2x, p2y ); printf ( " との距離は %f です。\n", point_distance ( p1x, p1y, p2x, p2y ) ); return 0; }
$ ./sample-005.exe 始点 ( 1.000000, 2.000000 ) と終点 ( 4.000000, 6.000000 ) との距離は 5.000000 です。 $
Download : sample-006.c
/* * 2018/11/30 sample-006.c */ /* * 平面上の点の操作 * * 利用方法 * コンパイル * cc -c sample-006.c * リンク * cc -o sample-006.exe sample-006.c -lm * 実行 * ./sample-006.exe */ #include <stdio.h> #include <math.h> /* sqrt を利用するので必要 (-lm も忘れずに ) */ /* * void print_point ( double px, double py ) * 「点」を表示する * double px -- 「点」の x 座標 * double py -- 「点」の y 座標 */ void print_point ( double px, double py ) { printf ( "( %f, %f )", px, py ); } /* * void mirror_x_point ( double py ) * x 軸に対し線対称の「点」の y 座標を求める * double py -- 「点」の y 座標 */ double mirror_x_point ( double py ) { return - py; } /* * void mirror_y_point ( double px ) * y 軸に対し線対称の「点」の x 座標を求める * double px -- 「点」の x 座標 */ double mirror_y_point ( double px ) { return - px; } /* * main */ int main( int argc, char *argv[] ) { double p1x = 1.0; /* p1 = ( 1.0, 2.0 ) */ double p1y = 2.0; double p2x; double p2y; /* x 軸に対して線対象 */ printf ( "点 " ); print_point ( p1x, p1y ); printf ( " と x 軸に対して線対称な点は " ); p2x = p1x; /* x 座標は変らない */ p2y = mirror_x_point ( p1y ); /* y 座標のみ計算 */ print_point ( p2x, p2y ); printf ( " となります。\n" ); /* y 軸に線対象 */ printf ( "点 " ); print_point ( p1x, p1y ); printf ( " と y 軸に対して線対称な点は " ); p2x = mirror_x_point ( p1x ); /* x 座標のみ計算 */ p2y = p1y; /* y 座標は変らない */ print_point ( p2x, p2y ); printf ( " となります。\n" ); return 0; }
$ ./sample-006.exe 点 ( 1.000000, 2.000000 ) と x 軸に対して線対称な点は ( 1.000000, -2.000000 ) となります。 点 ( 1.000000, 2.000000 ) と y 軸に対して線対称な点は ( -1.000000, 2.000000 ) となります。 $
Download : sample-007.c
/* * 2018/11/30 sample-007.c */ /* * 平面上の点の操作 * * 利用方法 * コンパイル * cc -c sample-007.c * リンク * cc -o sample-007.exe sample-007.c * 実行 * ./sample-007.exe */ #include <stdio.h> /* * void print_point ( double px, double py ) * 「点」を表示する * double px -- 「点」の x 座標 * double py -- 「点」の y 座標 */ void print_point ( double px, double py ) { printf ( "( %f, %f )", px, py ); } /* * void mirror_o_point_x ( double px ) * 原点に対し点対称の「点」の x 座標を求める * double px -- 「点」の x 座標 */ double mirror_o_point_x ( double px ) { return - px; } /* * void mirror_o_point_y ( double py ) * 原点に対し点対称の「点」の y 座標を求める * double py -- 「点」の y 座標 */ double mirror_o_point_y ( double py ) { return - py; } /* * main */ int main( int argc, char *argv[] ) { double p1x = 1.0; /* p1 = ( 1.0, 2.0 ) */ double p1y = 2.0; double p2x; double p2y; /* 原点に点対象 */ printf ( "点 " ); print_point ( p1x, p1y ); printf ( " と原点に対して点線対称な点は " ); /* x と y の処理を別々に行う.. */ p2x = mirror_o_point_x ( p1x ); p2y = mirror_o_point_y ( p1y ); print_point ( p2x, p2y ); printf ( " となります。\n" ); return 0; }
$ ./sample-007.exe 点 ( 1.000000, 2.000000 ) と原点に対して点線対称な点は ( -1.000000, -2.000000 ) となります。 $
Download : sample-008.c
/* * 2018/11/30 sample-008.c */ /* * 平面上の点の操作 * * 利用方法 * コンパイル * cc -c sample-008.c * リンク * cc -o sample-008.exe sample-008.c -lm * 実行 * ./sample-008.exe */ #include <stdio.h> #include <math.h> /* sqrt を利用するので必要 (-lm も忘れずに ) */ /* * void print_point ( double px, double py ) * 「点」を表示する * double px -- 「点」の x 座標 * double py -- 「点」の y 座標 */ void print_point ( double px, double py ) { printf ( "( %f, %f )", px, py ); } /* * void mirror_x_point ( double py ) * x 軸に対し線対称の「点」の y 座標を求める * double py -- 「点」の y 座標 */ double mirror_x_point ( double py ) { return - py; } /* * void mirror_y_point ( double px ) * y 軸に対し線対称の「点」の x 座標を求める * double px -- 「点」の x 座標 */ double mirror_y_point ( double px ) { return - px; } /* * main */ int main( int argc, char *argv[] ) { double p1x = 1.0; /* p1 = ( 1.0, 2.0 ) */ double p1y = 2.0; double p2x; double p2y; /* x 軸に対して線対象 */ printf ( "点 " ); print_point ( p1x, p1y ); printf ( " と x 軸に対して線対称な点は " ); p2x = p1x; /* x 座標は変らない */ p2y = mirror_x_point ( p1y ); /* y 座標のみ計算 */ print_point ( p2x, p2y ); printf ( " となります。\n" ); /* y 軸に線対象 */ printf ( "点 " ); print_point ( p1x, p1y ); printf ( " と y 軸に対して線対称な点は " ); p2x = mirror_x_point ( p1x ); /* x 座標のみ計算 */ p2y = p1y; /* y 座標は変らない */ print_point ( p2x, p2y ); printf ( " となります。\n" ); return 0; }
$ ./sample-008.exe 点 ( 1.000000, 2.000000 ) と x 軸に対して線対称な点は ( 1.000000, -2.000000 ) となります。 点 ( 1.000000, 2.000000 ) と y 軸に対して線対称な点は ( -1.000000, 2.000000 ) となります。 $
Download : sample-009.c
/* * 2018/11/30 sample-009.c */ /* * 平面上の点の操作 (構造体の利用例) * * 利用方法 * コンパイル * cc -c sample-009.c * リンク * cc -o sample-009.exe sample-009.c * 実行 * ./sample-009.exe */ #include <stdio.h> /* * 最初に、直交座標で「点」を表現する型を作ってしまう */ typedef struct { double x; /* 直交座標の x 座標を表すタグ名 */ double y; /* 直交座標の y 座標を表すタグ名 */ } Orthogonal; /* Orthogonal 型の宣言 */ /* * void print_point ( Orthogonal pt ); * 「点」を表示する * Orthogonal pt; 直交座標系で表現された「点」の座標 */ void print_point ( Orthogonal pt ) { /* * 構造体の要素は、タグ名を利用して参照できる */ printf ( "( %f, %f )", pt.x, pt.y ); } /* * Orthogonal mirror_o_point ( Orthogonal pt ) * 原点に対し点対称の「点」を求める * Orthogonal pt; 直交座標系で表現された「点」の座標 * 値 点対称の「点」を求める */ Orthogonal mirror_o_point ( Orthogonal pt ) { Orthogonal result; /* 返す値を入れる変数 */ result.x = - pt.x; /* 結果の x 座標は、元の x 座標の符号をかえた物 */ result.y = - pt.y; return result; /* 構造体の値が返せる */ } /* * main */ int main( int argc, char *argv[] ) { Orthogonal p1; Orthogonal p2; p1.x = 1.0; /* p1 = ( 1.0, 2.0 ) */ p1.y = 2.0; /* 原点に点対象 */ printf ( "点 " ); /* 構造体は引数で、そのまま渡せる */ print_point ( p1 ); printf ( " と原点に対して点線対称な点は " ); /* 構造体は、値としても取り出せるし、普通に代入もできる */ p2 = mirror_o_point ( p1 ); print_point ( p2 ); printf ( " となります。\n" ); return 0; }
$ ./sample-009.exe 点 ( 1.000000, 2.000000 ) と原点に対して点線対称な点は ( -1.000000, -2.000000 ) となります。 $
Download : sample-010.c
/* * 2018/11/30 sample-010.c */ /* * 名前を付けた点 * * 利用方法 * コンパイル * cc -c sample-010.c * リンク * cc -o sample-010.exe sample-010.c * 実行 * ./sample-010.exe */ #include <stdio.h> /* * 最初に、直交座標で「点」を表現する型を作ってしまう */ typedef struct { double x; /* 直交座標の x 座標を表すタグ名 */ double y; /* 直交座標の y 座標を表すタグ名 */ } Orthogonal; /* Orthogonal 型の宣言 */ /* * 更に、「名前付き」の「点」の型 */ typedef struct { char name; /* 点の名前 */ Orthogonal coordinate; /* 点の座標 */ } NPoint; /* * void print_point ( Orthogonal pt ); * 「点」を表示する * Orthogonal pt; 直交座標系で表現された「点」の座標 */ void print_point ( Orthogonal pt ) { /* * 構造体の要素は、タグ名を利用して参照できる */ printf ( "( %f, %f )", pt.x, pt.y ); } /* * void print_npoint ( NPoint npt ); * 名前付きの「点」を表示する * NPoint npt; 名前付きの「点」 */ void print_npoint ( NPoint npt ) { printf ( "点 %c の直交座標は ", npt.name ); print_point ( npt.coordinate ); printf ( "です。\n" ); } /* * main */ int main( int argc, char *argv[] ) { NPoint p; /* 点「P」*/ p.name = 'P'; /* 点「P」の名前は 'P' */ p.coordinate.x = 1.0; /* p.coordinate = ( 1.0, 2.0 ) */ p.coordinate.y = 2.0; print_npoint ( p ); /* 点「P」を表示 */ return 0; }
$ ./sample-010.exe 点 P の直交座標は ( 1.000000, 2.000000 )です。 $
Download : sample-011.c
/* * 2018/11/30 sample-011.c */ /* * 三次元空間内の点の操作 (構造体の利用例) * * 利用方法 * コンパイル * cc -c sample-011.c * リンク * cc -o sample-011.exe sample-011.c * 実行 * ./sample-011.exe */ #include <stdio.h> /* * 「名前付き」の空間の「点」の型 */ typedef struct { char name; /* 点の名前 */ double x; /* 直交座標の x 座標を表すタグ名 */ double y; /* 直交座標の y 座標を表すタグ名 */ double z; /* 直交座標の z 座標を表すタグ名 */ } NPoint3D; /* * void print_point3D ( NPoint3D npt ); * 「点」を表示する * NPoint3D npt; 直交座標系で表現された「点」の座標 */ void print_point ( NPoint3D pt ) { printf ( "点 %c の直交座標は ", pt.name ); printf ( "( %f, %f, %f )", pt.x, pt.y, pt.z ); printf ( "です。\n" ); } /* * NPoint3D mirror_o_point ( NPoint3D pt ) * 原点に対し点対称の「点」を求める * NPoint3D pt; 直交座標系で表現された「点」の座標 * 値 点対称の「点」を求める */ NPoint3D mirror_o_point ( char newName, NPoint3D pt ) { NPoint3D result; /* 返す値を入れる変数 */ result.name = newName; /* 名前は新しい物にする */ result.x = - pt.x; /* 結果の x 座標は、元の x 座標の符号をかえた物 */ result.y = - pt.y; /* 以下同様 */ result.z = - pt.z; return result; /* 構造体の値が返せる */ } /* * main */ int main( int argc, char *argv[] ) { NPoint3D p; NPoint3D q; p.name = 'P'; p.x = 1.0; /* P = ( 1.0, 2.0, 3.0 ) */ p.y = 2.0; p.z = 3.0; /* 原点に点対象 */ print_point ( p ); /* 構造体は、値としても取り出せるし、普通に代入もできる */ q = mirror_o_point ( 'Q', p ); printf ( "これと、原点に対して対称な、" ); print_point ( q ); return 0; }
$ ./sample-011.exe 点 P の直交座標は ( 1.000000, 2.000000, 3.000000 )です。 これと、原点に対して対称な、点 Q の直交座標は ( -1.000000, -2.000000, -3.000000 )です。 $
Download : sample-012.c
/* * 2018/11/30 sample-012.c */ /* * N 次元空間内の点の操作 (構造体/配列の利用例) * * 利用方法 * コンパイル * cc -c sample-012.c * リンク * cc -o sample-012.exe sample-012.c * 実行 * ./sample-012.exe */ #include <stdio.h> /* * 「名前付き」の空間の「点」の型 */ #define DIM 10 /* 10 次元 */ typedef struct { char name; /* 点の名前 */ double coordinate[DIM]; /* 直交座標の x 座標を表すタグ名 */ } NPointND; /* * void print_pointND ( NPointND npt ); * 「点」を表示する * NPointND npt; 直交座標系で表現された「点」の座標 */ void print_point ( NPointND pt ) { int dim; printf ( "点 %c の直交座標は ", pt.name ); printf ( "( " ); dim = 0; while ( dim < DIM ) { printf ( "%f", pt.coordinate[dim] ); if ( dim < DIM - 1 ) { printf ( ", " ); } dim++; } printf ( " )" ); printf ( "です。\n" ); } /* * NPointND mirror_o_point ( NPointND pt ) * 原点に対し点対称の「点」を求める * NPointND pt; 直交座標系で表現された「点」の座標 * 値 点対称の「点」を求める */ NPointND mirror_o_point ( char newName, NPointND pt ) { NPointND result; /* 返す値を入れる変数 */ int dim; result.name = newName; /* 名前は新しい物にする */ dim = 0; while ( dim < DIM ) { result.coordinate[dim] = - pt.coordinate[dim]; dim++; } return result; /* 構造体の値が返せる */ } /* * main */ int main( int argc, char *argv[] ) { NPointND p; NPointND q; int dim; p.name = 'P'; dim = 0; while ( dim < DIM ) { p.coordinate[dim] = dim; /* 浮動小数点型に整数値を入れると自動的に変換される */ dim++; } /* 原点に点対象 */ print_point ( p ); /* 構造体は、値としても取り出せるし、普通に代入もできる */ q = mirror_o_point ( 'Q', p ); printf ( "これと、原点に対して対称な、" ); print_point ( q ); return 0; }
$ ./sample-012.exe 点 P の直交座標は ( 0.000000, 1.000000, 2.000000, 3.000000, 4.000000, 5.000000, 6.000000, 7.000000, 8.000000, 9.000000 )です。 これと、原点に対して対称な、点 Q の直交座標は ( -0.000000, -1.000000, -2.000000, -3.000000, -4.000000, -5.000000, -6.000000, -7.000000, -8.000000, -9.000000 )です。 $
/* * 課題 CNAME-02 * * 20181116 20181116-02-QQQQ.c * * 標準入力から三つの整数値を読込み、それを小さい順に出力する (論理和) */ /* 課題 20181116-01 (if 文だけで頑張る)に対し、 論理演算子を使って、実現する */ #include <stdio.h> /* * main */ int main( void ) { int i1; /* 三つの整数値を入力するので、三つの整数型変数(i1〜i3)を準備する */ int i2; int i3; int o1; /* 結果も三つの整数値なので、同様に三つ (o1〜o3) を準備する */ int o2; int o3; /* * とりあえず、三つの整数値を標準入力から入力 */ /* 一つ目整数値の入力 */ printf ( "一つ目の整数値を入力してください : " ); scanf ( "%d", &i1 ); /* 二つ目整数値の入力 */ printf ( "二つ目の整数値を入力してください : " ); scanf ( "%d", &i2 ); /* 三つ目整数値の入力 */ printf ( "三つ目の整数値を入力してください : " ); scanf ( "%d", &i3 ); /* 前の課題では、if 構文の条件の所に、 二つの値の比較しかできなかった...(が、対象が三つなので..) => 複数の if 構文を組み合わせ(if 構文の入れ子)で実現 論理演算子が使えると、 一つの条件分岐(if 構文)の判断に、より複雑な条件がかける => 三つの数の関係を一つの条件式でチェック可能 if ( P ) { if ( Q ) { C1 } else { C2 } } else { C2 } <=> if ( P && Q ) { C1 } else { C2 } && はなくてもよいが、&& を使うと、わかりすくなる場合がある */ /* * 入力された整数値の大小により、小さい順に変数 o1 〜 o3 に値を代入 */ /* 最初に、まず、一番小さい数をみつけてしまう */ if ( (i1 < i2) && (i1 < i3) ) {/* i1 の値は i2, i3 の双方の値より小さい */ o1 = i1; /* 一番小さいのは i1 の値 */ if ( i2 < i3 ) { /* 残る二つの値を比較 */ o2 = i2; o3 = i3; } else { o2 = i3; o3 = i2; } } else if ( (i2 < i1) && (i2 < i3) ) { /* i2 の値が.. */ o1 = i2; /* 一番小さいのは i2 の値 .. */ if ( i1 < i3 ) { /* 残る二つの値を比較 */ o2 = 12; o3 = i3; } else { o2 = i3; o3 = i1; } } else { /* 残る可能性は i3 の値が最少ってこと */ o1 = i3; /* 一番小さいのは i2 の値 .. */ if ( i1 < i2 ) { o2 = i1; o3 = i2; } else { o2 = i2; o3 = i1; } } /* * 変数 o1 〜 o3 に整数値が小さい順に入っているので、それを出力 */ printf ( "入力された値を小さい順に並べると %d, %d, %d になります。\n", o1, o2, o3 ); return 0; }
/* * 課題 CNAME-03 * * 20181116 20181116-03-QQQQ.c * * 真偽表の作成 * */ #include <stdio.h> /* * main : 真偽表の作成 */ int main(void) { int P; /* 論理値は、整数値なので、整数変数を二つ (P,Q) 利用 */ int Q; /* 真偽値表 */ printf ( "+-------+----+-------------+\n" ); printf ( "| 論理値|否定|論理積|論理和|\n" ); printf ( "+---+---+----+------+------+\n" ); printf ( "| P | Q | !P | P&&Q | P||Q |\n" ); printf ( "+---+---+----+------+------+\n" ); for ( P = 0; P <= 1; P++ ) { /* これで P = 0(偽), 1(真) となる */ for ( Q = 0; Q <= 1; Q++ ) { /* これで Q = 0(偽), 1(真) となる */ printf ( "| %d | %d | %d | %d | %d |\n", P, Q, !P, P&&Q, P||Q ); } } printf ( "+---+---+----+------+------+\n" ); return 0; }
/* * 課題 CNAME-04 * * CDATE FILENAME * * ド・モルガンの定理 !(P&&Q) == (!P)||(!Q) * */ #include <stdio.h> /* * not_and ( P, Q ) == !(P&&Q) */ int not_and ( int P, int Q ) { return !(P&&Q); /* 仕様通り */ } /* * or_not ( P, Q ) == (!P)||(!Q) */ int or_not ( int P, int Q ) { return (!P)||(!Q); /* 仕様通り */ } /* * main : 真偽表の作成 */ int main(void) { int P; /* 論理値は、整数値なので、整数変数を二つ (P,Q) 利用 */ int Q; /* 真偽値表 */ printf ( "+---+---+---------+------------+\n" ); printf ( "| P | Q | !(P&&Q) | (!P)||(!Q) |\n" ); printf ( "+---+---+---------+------------+\n" ); for ( P = 0; P <= 1; P++ ) { /* これで P = 0(偽), 1(真) となる */ for ( Q = 0; Q <= 1; Q++ ) { printf ( "| %d | %d | %d | %d |\n", P, Q, not_and(P,Q), or_not(P,Q) ); } } printf ( "+---+---+---------+------------+\n" ); return 0; }
#include <stdio.h> int P(char ch) { printf ( "P で ch(%c) が 'a' 以上かチェック\n", ch ); return 'a' <= ch; /* ch が 'a' 以上だと 1(真) そうでないと 0 (偽) */ } int Q(char ch) { printf ( "Q で ch(%c) が 'z' 以下かチェック\n", ch ); return ch <= 'z'; /* ch が 'z' 以下だと 1(真) そうでないと 0 (偽) */ } /* 「P(ch) && Q(ch)」 「'a' <= ch && ch <= 'z'」 => ch が小文字 */ int main(int argc, char *argv[]) { char ch = getchar(); /* キーボードより一文字入力 */ if ( P(ch) && Q(ch) ) { // ショートカット printf ( "%c は、英小文字です。\n", ch ); } else { printf ( "%c は、英小文字ではありません。\n", ch ); } /* 関数 P, Q に副作用がある 入力 f Pで条件が成立し、 Q が実行され、Q でも成立するので「真」 } Pで条件が成立し、 Q が実行されるが、Q では不成立なので「偽」 F Pで条件が不成立 Q が実行されずに、全体として「偽」 ! Q が実行されない事がある ! 副作用がある場合原理的に P&&Q と Q&&P が異なる ! => 数学の立場としては気持ちが悪い */ return 0; }
/* * 2018/11/30 sample-001.c */ /* * 銀行口座への振込プログラム * * 利用方法 * コンパイル * cc -c sample-001.c * リンク * cc -o sample-001.exe sample-001.c * 実行 * ./sample-001.exe */ #include <stdio.h> /* * main * * 現実の世界 プログラムの世界 * * [表現] 栗野の口座 kurino_account * * [事前] 100 万円 kurino_account = 1000000 * * 振込額 10 万円 transfer_money = 100000 * <振込> kurino_account = kurino_account + transfer_money * [事後] 110 万円 * * <振込> という「情報上の機能」 <足し算> という「数値上の操作」 */ int main( int argc, char *argv[] ) { int kurino_account = 1000000; /* 栗野の銀行口座に 100 万円入っている */ int transfer_money = 100000; /* 10 万円の振込をしたい.. */ printf ( "現在の栗野の残高は %d 万円です。\n", kurino_account / 10000 ); /* <振込> を行うプログラム */ printf ( "栗野の口座に %d 万円の振込を行います。\n", transfer_money / 10000 ); /* 「足し算」が「振込」になる */ kurino_account = kurino_account + transfer_money; printf ( "現在の栗野の残高は %d 万円です。\n", kurino_account / 10000 ); return 0; }
/* * 2018/11/30 sample-003.c */ /* * 平面上の「点」の二つの表現銀行口座への振込プログラム * * 利用方法 * コンパイル * cc -c sample-003.c * リンク ( M_PI,sin,cos を利用するので 「-lm」が必須 ) * cc -o sample-003.exe sample-003.c -lm * 実行 * ./sample-003.exe */ #include <stdio.h> #include <math.h> /* sin, cos を利用するので.. */ /* * void print_orthogonal ( char name, double x, double y ) * 直交座標の表示 * char name; 点の名前 * double x; 直交座標の X 座標 * double y; 直交座標の Y 座標 */ void print_orthogonal ( char name, double x, double y ) { printf ( "点 %c の直交座標は (%f,%f) です。\n", name, x, y ); } /* * 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[] ) { /* 点 P : 座標 (2,3) */ double P_orthogonal_x = 2.0; /* 点 P の直交座標系の x 座標 */ double P_orthogonal_y = 3.0; /* 点 P の直交座標系の y 座標 */ double P_polar_radius; /* 点 P の極座標系の動径 */ double P_polar_argument; /* 点 P の極座標系の偏角 */ /* 点 Q : 原点から 7 離れており、角度は x 軸に対して 60 度 ( Pi/3 ) */ double Q_orthogonal_x; /* 点 Q の直交座標系の x 座標 */ double Q_orthogonal_y; /* 点 Q の直交座標系の y 座標 */ double Q_polar_radius = 7.0; /* 点 Q の極座標系の動径 */ double Q_polar_argument = M_PI/3; /* 点 Q の極座標系の偏角 */ /* * 点 P の表示 */ print_orthogonal ( 'P', P_orthogonal_x, P_orthogonal_y ); /* * r = \sqrt{x^2+y^2} なので */ P_polar_radius = sqrt ( P_orthogonal_x * P_orthogonal_x + P_orthogonal_y * P_orthogonal_y ); /* * a = \tan^{-1}{y/x} なので * cf. http://www1.cts.ne.jp/~clab/hsample/Math/Math2.html */ P_polar_argument = atan ( P_orthogonal_y / P_orthogonal_x ); print_polar ( 'P', P_polar_radius , P_polar_argument ); /* * 点 Q の表示 */ print_polar ( 'Q', Q_polar_radius, Q_polar_argument ); /* * x = r \cos{a} なので */ Q_orthogonal_x = Q_polar_radius * cos( Q_polar_argument ); /* * y = r \sin{a} なので */ Q_orthogonal_y = Q_polar_radius * sin( Q_polar_argument ); print_orthogonal ( 'Q', Q_orthogonal_x, Q_orthogonal_y ); return 0; }
[前回の内容] for 構文/while 構文(/switch 構文) break 構文の範囲を終了して、次の文に移る !! for/while は繰り返しの先頭で判断をする !! 1. 繰り返し本体に入る前にチェックする !! => 繰り返し本体内では、そのチェックした条件が成立 !! 2. 抜けるとしたら、先頭で !! => break は、途中で抜ける事が可能 continue(ループのみ) 構文の「最後」に移動する => 次のループ(繰り返しのステップ)に入る => for 構文では、パラメータの更新を行い、 更に、while 構文と同様、条件チェックを行う => if 構文を利用して、continue は除去できる # continue : retry (skip) になっていて、なくてもよい # break; ループを途中で中断(quit)して抜けるための、常套手段 # => こちらも、if 構文を利用して、除去は可能だが、非自明 条件式の正体 前回までは「条件(論理式)」=>「命題」なんだろうと思っていた 実態は、単なる「整数値(0 => 偽 / 0 以外 => 真)」 => 計算したり、関数の引数にできる、関数の値にできる.. ! 関数の引数に渡す「条件式の値」は、関数内で「フラグ引数」として ! 扱われる ! => 条件判断の結果を、関数の外で判断し、関数の中で利用する ! => 条件判断の結果は、「どの命令を実行するか」という事 ! => コードの実行を、データで制御できる ! !! プログラムのシステム : コードがデータを処理する 条件式が「整数値」を持つ 様々な計算が可能 整数としての計算もできる(あまりお勧めしない..) 論理式としての計算も可能 論理演算子が使える (P,Q が共に条件式[論理式]) && 論理積 P&&Q => PとQの一方が0なら0、それ以外は1 || 論理和 P||Q => PとQの一方が0でないなら1、それ以外は0 論理表 P | Q | P&&Q | P||Q T T T(1) T(1) T F F(0) T(1) F T F(0) T(1) F F F(0) F(0) T : 真 ( 0 以外 ) F : 偽 ( 0 ) ||, && は、ショートカットの機能がある P&&Q の場合、P が 0 の時は、Qを評価せずに、全体を 0 とする P||Q の場合、P が 0 以外なら、Q を評価せず、全体を 1 とする 1. 効率が良い 2. Q に副作用がある場合は、意味が変わってくる [今回] ! (否定演算子) !P => P が 0 なら 1 で、それ以外は 0 論理表 P | !(P) | (P) == 0 T F(0) F(0) F(0) T(1) T(1) # !P の代わりに P==0 と書いても、同じ結果 # => やっぱり、P が条件式の時は、否定を表すのに !P の方が読みやすい # その一方、P が条件式でない場合は、 P==0 の代わりに !P を使うのは、読みにくくなるのでやめた方がよい == [データ構造] 今まで、プログラム構造 プログラム : 命令を組み合わせて、機能を実現する 組み合わせ方法: 順接/条件分岐/繰り返す A;B; / if 構文, switch 構文 / 再帰, while, for => コード(命令)の組み合わせで、データ(情報)を操作する コードの組み合わせができれば、データの加工の手段も決まる 最終的なゴール : 必要とする情報を得る プログラミングは、その目的とする情報を得るためのコード組み合わせと行う (コードの実行を、データで制御できる) => データの組み合わせで、目的が実現できる可能性がある [利点] プログラム : コードの並びは プログラマしか変更できない プログラミング時には変更可能だが、「実行時には変更できない」 データ: 実行時に決める事ができる(プログラミング時も可能だが..) プログラムの利用者が変更可能 データの方が、柔軟性があって、扱いやすい [観点] プログラムは、データを命令として処理して、 そのデータの組み合わせを命令の組み合わせと解釈する(インタープリター:通訳)物として側面がある => この側面(データを結果を求める処理の対象ではなく、コードを実行するための命令ととらえる)を進めると、柔軟性を増させる可能性がでてくる => C 言語(に限らず、いろいろな言語)が、「万能性を持つ」根拠になっている # 定義「言語 A が万能である」 # 他の任意の言語 B が記述できるプログラムと同じ機能を、言語 A でも実現できる # 例 : C 言語は万能なので、他の言語(python)で記述されたプログラムと同じ結果をもたらす、プログラムが作れる # [証明(概略)] # その言語で、他の言語のインタープリター(他の言語でかかれたプログラムと、その入力データを、ともに二つのデータとして扱い、プログラム部分を解釈して、入力データを処理する)が作れるから.. プログラムを書く上では、プログラムの構造(プログラムそのもの)も 大事だが、データ構造(間接的にプログラムの構造を定めうるもの)も大事 「プログラムをデータを中心に考える」という方針 利点:柔軟性があり、高度な処理可能 欠点:効率が悪い、理解が難しい 定義「データ構造」とは何か 「データの組み合わせ」と、その操作方法の事 複数のデータを一つのデータとしてまとめる役割がある 例: 平面上の点の座標: x 座標(整数型) と y 座標(整数型)の二つの整数型の組み合わせで、実現できる [sample-003.c] 「平面上の『点』」を扱いたい 扱う : 『点』を移動する 平行移動 点対称移動 面対象移動 この「操作」を「プログラムで実現する」というどういう事か 対象(『点』)を表す「データ」を作り、 その「データ」を変更する事によって、実現する [sample-001.c] 「銀行の振込」という『機能』を実現 現実の世界の情報を担う変数(kurino_account)を作り それの数値的な操作 間接的に行っている プログラムが「機能」する 1. プログラムのデータの計算が、現実の情報操作に対応をする プログラムを作る人の仕事 2. データと情報が結びついている プログラムを使う人の仕事 プログラム上のデータは、現実世界の情報を模している必要がある 対応関係の事をコーディングと呼ぶ => プログラマは、うまいコーディングを選択する必要がある 「うまい」 少ない操作/わかりやすい操作で、目的の機能が実現できる 例: 平面上の点 P 直交座標系 (x,y) で表現 => この表現で、表すと都合がよいことが多いから 「平行移動」が簡単に実現できる それぞれの座標の加算で実現できて、加算はコンピュータで、即実現できる 「回転移動」は、行列の掛け算が必要 一方 極座標 「回転移動」は、簡単 しかし、平行移動は簡単じゃない コーディングによって、プログラムの実現難易度が異なる コーディング 現実の情報をデータに対応づける規則 その対応づけるデータの構造の良しあしで、 そのデータ構造を操作するプログラムの難易度が異なってくる => データ構造の重要性
課題プログラム内の「/*名前:ここ*/」の部分を書き換え「/*この部分を完成させなさい*/」の部分にプログラムを追加して、プログラムを完成させます。
Download : 20181130-01.c
/* * 課題 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 の計算 */ /* 対称なので原点から距離は同じ */ /* ** この部分を完成させなさい */ /* 180(π)だけ回転 */ /* ** この部分を完成させなさい */ /* * 点 R の表示 */ print_polar ( 'R', R_polar_radius, R_polar_argument ); return 0; }
$ ./20181130-01-QQQQ.exe 点 Q の極座標は (7.000000,1.047198) です。 点 R の極座標は (7.000000,4.188790) です。 $
Download : 20181130-02.c
/* * 課題 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 { 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 ); } /* * 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 を加えればよい */ /* ** この部分を完成させなさい */ /* 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 ); /* 構造体は、値としても取り出せるし、普通に代入もできる */ /* ** この部分を完成させなさい */ print_point ( p2 ); printf ( " となります。\n" ); return 0; }
123 987 456
$ ./20181130-02-QQQQ.exe 点 ( 1.000000, 2.000000 ) を x 軸方向に 10.000000, y 軸方向に -100.000000 移動した点は ( 11.000000, -98.000000 ) となります。 $
Download : 20181130-03.c
/* * 課題 20181130-03 * * 20181130 20181130-03-QQQQ.c * * 3 次元ベクトルの差の計算 * */ #include <stdio.h> /* * 3 次元ベクトル */ typedef struct { /* 3 次元ベクトル */ double x; /* x 要素 */ double y; /* y 要素 */ double z; /* z 要素 */ } Vector3D; /* 新しい型 : Vector3D */ /* * void print_Vector3D ( Vector3D v ) * ベクトルの内容を書き出す * Vector3D v; 書き出すベクトル */ void print_Vector3D ( Vector3D v ) { printf ( " %f\n", v.x ); /* v の x 要素の出力 */ printf ( "( %f )\n", v.y ); /* v の y 要素の出力 */ printf ( " %f\n", v.z ); /* v の z 要素の出力 */ /* TeX で表現するならば、 printf ( "\\left(\\begin{array}{c} %f \\\\ %f \\\ %f \\end{array}\\right)\n", v.x, v.y, v.z ); などととすればよい。 */ } /* * Vector3D sub_Vector3D ( Vector3D dst, Vector3D src ) * 二つのベクトルの差を計算する * Vector3D dst; 引かれるベクトル * Vector3D src; 引くベクトル * 帰り値 二つのベクトルの差となるベクトル */ Vector3D sub_Vector3D ( Vector3D dst, Vector3D src ) { Vector3D result; /* 計算結果(差)を收める変数 */ /* ** この部分を完成させなさい */ /* x 成分の計算 */ result.y = dst.y - src.y; /* y 成分の計算 */ /* ** この部分を完成させなさい */ /* z 成分の計算 */ return result; /* 計算した結果を値として返す */ } /* * main */ int main( int argc, char *argv[] ) { Vector3D dst; Vector3D src; dst.x = 1.2; /* 1.2 */ dst.y = 2.3; /* dst = ( 2.3 ) */ dst.z = 3.4; /* 3.4 */ src.x = -9.8; /* -9.8 */ src.y = 8.7; /* dst = ( 8.7 ) */ src.z = 0.0; /* 0.0 */ print_Vector3D ( dst ); /* dst の出力 */ printf ( "と\n" ); print_Vector3D ( src ); /* src の出力 */ printf ( "の差は\n" ); print_Vector3D ( sub_Vector3D ( dst, src ) ); printf ( "となります。\n" ); return 0; }
$ ./20181130-03-QQQQ.exe 1.200000 ( 2.300000 ) 3.400000 と -9.800000 ( 8.700000 ) 0.000000 の差は 11.000000 ( -6.400000 ) 3.400000 となります。 $