/* * 20210430-02-QQQQ.c * 底辺の長さが指定した文字列の二倍の長さ - 1 の横向のピラミッドを作成するプログラムを作成する */ #include <stdio.h> #include <string.h> /* */ void down ( char *edge ) { if ( !strcmp ( edge, "" ) ) { /* なにもする必要はない */ } else { printf ( edge ); printf ( "\n" ); down ( edge + 1 ); } } void up ( char *edge ) { if ( !strcmp ( edge, "" ) ) { /* なにもする必要はない */ } else { up ( edge + 1 ); /* 先に再帰呼び出し */ printf ( edge ); /* 後から繰り返す命令 */ printf ( "\n" ); } } void pyramid ( char *edge ) { /* pyramid ( "*****" ) * ** *** **** ***** **** *** ** * => * ** *** **** => up ***** => printf **** = down *** ** * */ /* ピラミッドの上のだんだん、増えてゆく階段 */ up ( edge + 1 ); /* ピラミッドの頂点の部分 */ printf ( edge ); printf ( "\n" ); /* ピラミッドの下のだんだん、減ってゆく階段 */ down ( edge + 1 ); } /* * main */ int main ( void ) { pyramid ( "*****" ); printf ( "\n" ); pyramid ( "**********" ); return 0; } /* pyramid ( "*****" ); => * ** *** **** ***** **** *** ** * => * ** *** **** ***** => printf ( "*****" ); **** => p-007.c と同じ仕組み *** ** * == * ** *** **** cf. **** *** ** * => **** printf + *** 再帰 ** * => f () => printf() f() g() * ** *** g() + **** printf() */
/* * 20210507-01-QQQQ.c * 一つ目の引数の文字列の長さの個数だけ、 * 二つ目の引数の文字列を出力する関数の作成 */ #include <stdio.h> #include <string.h> /* strcmp を利用するために必要 */ /* * n_times_print_string ( char *times, char *string ) * times : 繰返し回数を表す文字列 * string : 出力する文字列 * => 再帰と if 構文の組み合わせで、 * 有限回(一つ目の引数の文字列の長さ)だけ繰り返す */ void ntimes_print_string ( char *ntimes, char *string ) { if ( !strcmp ( ntimes, "" ) ) { /* ntimes の長さが 0 */ /* 何もする必要はない (関数は終了) */ } else { /* そうでなければ */ /* 二つ目の文字列を出力 */ printf ( string ); /* この命令が繰り返される */ /* 再帰を利用して、残りの回数だけプリント */ /* 一つ目の引数は、長さを減らす必要がある */ /* 二つ目の引数は、そのまま渡してよい (そのまま引き継ぐ) */ /* 再帰呼び出しをする */ ntimes_print_string ( ntimes + 1, string ); } } /* * main */ int main ( void ) { /* "Hello, World\n" を 5 回数 */ ntimes_print_string ( "12345", "Hello, World\n" ); /* "こんにちは、皆さん\n" を 8 回数 */ ntimes_print_string ( "12345***", "こんにちは、皆さん\n" ); return 0; }
#include <stdio.h> #include <string.h> int main(void) { printf ( "if 構文 [1]\n" ); if ( !strcmp( "abc", "abc" ) ) { printf ( "条件成立\n" ); /* こちらが実行される */ } else { printf ( "条件不成立\n" ); } printf ( "if 構文 [2]\n" ); if ( !strcmp( "abc", "ABC" ) ) { printf ( "条件成立\n" ); } else { printf ( "条件不成立\n" ); /* こちらが実行される */ } return 0; }
#include <stdio.h> void recfunc(void) { printf ( "recfunc を呼び出しました\n" ); printf ( "これは、recfunc の中の prinf 関数です\n" ); printf ( "再帰呼び出しする部分は、何もしていないように見える\n" ); recfunc(); /* 関数 recfunc の中で自分自身(recfunc)を呼び出す */ /* => 再帰呼び出し */ } int main(void) { recfunc(); /* 関数呼び出し */ /* => printf ( "recfunc を呼び出しました\n" ); => 画面に「recfunc を呼び出しました」表示する recfunc(); => printf ( "recfunc を呼び出しました\n" ); => 画面に「recfunc を呼び出しました」表示する recfunc(); ... 無限に続く => 「recfunc を呼び出しました」が無限に表示される事になる */ return 0; }
#include <stdio.h> #include <string.h> void setbell( char *day ) { if ( !strcmp ( day, "" ) ) { /* 繰り返しをしない */ /* 何もしない <= ベルをなさらない */ printf ( "一日がはじまりました\n" ); } else { /* 繰り返しをする */ printf ( "一日がはじまりました\n" ); setbell( day + 1 ); } } int main(void) { /* ***** (5) 月 ***** (4) 火 .. (0) 土 => ベルを設定しない */ setbell( "*****" ); /* 関数呼び出し */ return 0; }
#include <stdio.h> #include <string.h> /* repeat は、引数で指定された文字列の長さの回数だけ、 自分自身を(再帰)呼び出す関数 */ void repeat( char *times ) { if ( !strcmp( times, "" ) ) { /* 何もしない */ } else { repeat ( times + 1 ); /* 再帰呼び出し */ /* 引数の文字列の長さを短くする */ } } int main(void) { repeat( "123456" ); /* 引数に繰り返したい回数と同じ長さの文字列を指定 */ /* => times <- "123456" if ( !strcmp( times, "" ) ) { } else { repeat ( times + 1 ); } => if ( !strcmp( "123456", "" ) ) { } else { repeat ( "123456" + 1 ); } => repeat ( "23456" ); => times <- "23456" if ( !strcmp( times, "" ) ) { } else { repeat ( times + 1 ); } => if ( !strcmp( "23456", "" ) ) { } else { repeat ( "23456" + 1 ); } => repeat ( "3456" ); => repeat ( "456" ); => .. => repeat ( "" ); => times <- "" if ( !strcmp( times, "" ) ) { } else { repeat ( times + 1 ); } => if ( !strcmp( "", "" ) ) { } else { repeat ( "" + 1 ); } => なにもなくなってしまう */ return 0; }
#include <stdio.h> #include <string.h> /* repeat は、引数で指定された文字列の長さの回数だけ、 自分自身を(再帰)呼び出す関数 */ void repeat( char *times ) { if ( !strcmp( times, "" ) ) { /* 何もしない */ } else { printf ( "繰り返したい命令\n" ); /* 繰り返す命令 */ repeat ( times + 1 ); /* 再帰呼び出し */ /* 引数の文字列の長さを短くする */ } } int main(void) { repeat( "123456" ); /* 引数に繰り返したい回数と同じ長さの文字列を指定 */ return 0; }
#include <stdio.h> #include <string.h> /* repeat は、引数で指定された文字列の長さの回数だけ、 自分自身を(再帰)呼び出す関数 */ void repeat( char *times ) { if ( !strcmp( times, "" ) ) { /* 何もしない */ } else { printf ( times ); /* times 自身を出力する */ printf ( "\n" ); repeat ( times + 1 ); /* 再帰呼び出し */ /* 引数の文字列の長さを短くする */ } } int main(void) { repeat( "123456" ); /* 引数に繰り返したい回数と同じ長さの文字列を指定 */ /* => times <- "123456" if ( !strcmp( times, "" ) ) { } else { printf ( times ); printf ( "\n" ); repeat ( times + 1 ); } => if ( !strcmp( "123456", "" ) ) { } else { printf ( "123456" ); printf ( "\n" ); repeat ( "123456" + 1 ); } => printf ( "123456" ); printf ( "\n" ); => 画面に「123456<改行>」が表示される repeat ( "23456" ); => => 画面に「23456<改行>」が表示される repeat ( "3456" ); .. => 画面に「6<改行>」が表示される repeat ( "" ); => 終了 */ return 0; }
#include <stdio.h> #include <string.h> /* repeat は、引数で指定された文字列の長さの回数だけ、 自分自身を(再帰)呼び出す関数 */ void repeat( char *times ) { if ( !strcmp( times, "" ) ) { /* 何もしない */ } else { printf ( times ); /* times 自身を出力する */ printf ( "\n" ); repeat ( times + 1 ); /* 再帰呼び出し */ /* 引数の文字列の長さを短くする */ } } int main(void) { repeat( "123456" ); /* 引数に繰り返したい回数と同じ長さの文字列を指定 */ /* => S6= 123456 23456 3456 456 56 6 S6 はどうやってつくればよいか ? # times = "123456" [1] 目的を分割し、分割したものがそれぞれどうなるかを調べる repeat( "123456" ) S6= 123456 => times そのものなので、printf(times) + 23456 => S5 3456 => repeat( "23456" ) [再帰] 456 => repeat( "123456" + 1 ) 56 => repeat( times + 1 ) 6 => [2] 分割したものが、共通の変数を持つように整理する repeat( "123456" ) = printf ( "123456" ) + printf ( "\n" ) + repeat( "123456" + 1 ) => [3] 変数を用いて、再帰部分の命令を定める repeat( times ) = printf ( times ) + printf ( "\n" ) + repeat( times + 1 ) [4] 文字列が空文字列になった場合の、条件分岐を作る -> 関数の定義部分が定まる if ( !strcmp( times, "" ) ) { } else { printf ( times ); printf ( "\n" ); repeat ( times + 1 ); } */ return 0; }
#include <stdio.h> #include <string.h> /* repeat は、引数で指定された文字列の長さの回数だけ、 自分自身を(再帰)呼び出す関数 */ void repeat( char *times ) { if ( !strcmp( times, "" ) ) { /* 何もしない */ } else { repeat ( times + 1 ); /* 再帰呼び出し */ /* 引数の文字列の長さを短くする */ printf ( times ); /* times 自身を出力する */ printf ( "\n" ); } } int main(void) { repeat( "123456" ); /* 引数に繰り返したい回数と同じ長さの文字列を指定 */ /* => S6= 123456 23456 3456 456 56 6 S6 はどうやってつくればよいか ? # times = "123456" [1] 目的を分割し、分割したものがそれぞれどうなるかを調べる repeat( "123456" ) S6= 123456 => times そのものなので、printf(times) + 23456 => S5 3456 => repeat( "23456" ) [再帰] 456 => repeat( "123456" + 1 ) 56 => repeat( times + 1 ) 6 => [2] 分割したものが、共通の変数を持つように整理する repeat( "123456" ) = printf ( "123456" ) + printf ( "\n" ) + repeat( "123456" + 1 ) => [3] 変数を用いて、再帰部分の命令を定める repeat( times ) = printf ( times ) + printf ( "\n" ) + repeat( times + 1 ) [4] 文字列が空文字列になった場合の、条件分岐を作る -> 関数の定義部分が定まる if ( !strcmp( times, "" ) ) { } else { printf ( times ); printf ( "\n" ); repeat ( times + 1 ); } */ return 0; }
#include <stdio.h> void oneAdd ( char *a, char *b ) { printf ( a ); printf ( b ); } int main(void) { oneAdd( "**", "***" ); printf ( "\n" ); return 0; }
#include <stdio.h> #include <string.h> void oneMul ( char *a, char *b ) { /* b の長さだけ、a の出力を繰り返す */ if ( !strcmp ( b, "" ) ) { /* Do Nothing */ } else { printf ( a ); oneMul ( a, b + 1 ); } } int main(void) { oneMul( "**", "***" ); /* 2*3 = 6 */ printf ( "\n" ); oneMul( "***", "****" ); /* 3*4 = 12 */ printf ( "\n" ); return 0; }
#include <stdio.h> #include <string.h> void oneSub ( char *a, char *b ) { /* a-b a >= b の時に a-b を出力 そうでなかったときは、何も出力しない a-0 = a => a をそのままだせばよい a-b = (a-1)-(b-1) => a-b の代わりに (a-1)-(b-1) をだせばよい */ if ( !strcmp ( b, "" ) ) { printf ( a ); } else { oneSub ( a + 1, b + 1 ); } } int main(void) { oneSub( "***", "**" ); /* 3-2 = 1 */ printf ( "\n" ); oneSub( "*****", "**" ); /* 5-2 = 3 */ printf ( "\n" ); return 0; }
前回(2021/05/07)の内容 # 「文字列」:「"(ダブルクォーテーション)"」挟む # 例 : 「"abc"」: 三つの文字 a, b, c からなる長さ 3 の文字列 「文字」の扱い 文字の表現 ( 「'(シングルクォーテーション)」で挟む ) 例: 「'a'」: 「a」という文字を表す a, "a", 'a' 変数名(関数名), 文字列, 文字 => すべて異なるものとして扱われる !!! 「変数」は、「何か(値)」に置き換わる !!! "a", 'a' : それ自身(値)を表す表記 ( 即値/定数 ) !! 「文字」は「文字列」と違うので、 !! => 違う扱いをする必要がある 文字の出力 ( putchar 関数を使う ) # cf. 文字列の出力には printf 関数を利用する # ! 「出力」=「画面に描画する」 # 文字扱いに関しては、追加を今日の後でやる 条件分岐 二種類の命令列のどちらか一方を、 条件によって、選択して実行する機能 # cf. # 順接: 複数の命令を、並べた順にすべて実行する # 命令A 命令B => 命令Aが実行された後に、命令Bが実行される # => 命令を書けば、書いた分だけ実行される if 構文 if ( 条件 ) { 命令 A } else { 命令 B } => 条件が成立した時には、命令 A を そうでなかった(条件が不成立)時には命令 B を実行する => 命令 A, B の両方を書いても、 A, B のどちらか一方しか実行されない if 構文単独だと、 二つのうちの一つしか選べない 三つ以上の命令のどれかを選ぶ場合は、 if 構文を組み合わせる 一つの事に関して、複数の条件をチェックする場合 else if 表現 if ( 条件1 ) { 命令 1 } else if ( 条件 2 ) { 命令 2 } ... } else if ( 条件 n ) { 命令 n } else { 命令 n+1 } => (条件 1 ? k-1 が不成立で..) 条件 k が成立すると、命令 k が実行される いずれも成立しない場合は、 命令 k+1 が実行される strcmp 関数 引数に与えられた二つの文字列を比較する !strcmp(A,B) A, B が同じ時に、「条件成立」という意味を表す表現 関数に引数を与える事により、 後から引数の値を変更して、関数の振る舞いを変える事ができる 単に、変数(と順接)を利用するだけだと、 関数の振る舞いは、「連続的」である => 値が大きく変われば、振る舞いも大きく変わる 値が小さく変われば、振る舞いも小さく変わる <= if 構文(条件分岐)を利用すると、 値を少し変えるだけで、 関数の振る舞いを大きくかえる事ができる => 不連続なふるまい 再帰呼出し 関数の定義の中で、自分自身を呼び出す => 再帰呼び出しの命令以外の部分の命令が繰り返される事になる 「(命令の..)繰り返し(loop)」が(再帰呼び出しを利用して..)表現できる C 言語(に限らず、プログラミング言語一般で..) 基本的な命令(単語) => 言語によっていろいろ 基本的な命令を組み合わせて(文法)、新しい命令を作る(制御構造)仕組みが 順接: A と B => A;B or B;A 二つの命令を順に実行する命令が作られる 条件分岐 : A と B (と、条件) => if (条件) {A} else {B} によって、 (条件によって) A, B のどちらか一方を実行する命令が作れる 繰り返し : A => func() { A func() } によって、 A を何度も繰り返す、命令が作れる !! 制御構造は、この三種類あれば、任意のプログラムが作れる !! => 万能性 順接 A, B => A,B 条件分岐 A, B => A B 繰り返し A => A, A, A, .., A, ... <= いきなり、有限回から無限回に飛躍するのは、使いずらい => 量が増える ( 1 回かいただけで、複数回実行される ) のはうれしいが、 それでも有限にしたい 再帰と条件分岐を組み合わせると、 有限回の繰り返しが可能なる mathematica について 昨年、一年生の時にインストールした mathematica は、 今年度4月いっぱいで、ライセンスがきれます。 今年度も利用したい場合は、 新しく、インストールする ライセンス情報を入力する => 大学でしかできない 作業が必要です。 金曜の昼休みにうけつけますので、申し出てください == 再帰と if 構文を組み合わせる => 有限回の繰り返しが可能 p-005.c をテンプレート(イデオム)として、 引数で指定された文字列の長さだけ、 命令を繰り返す事が可能 p-006.c, p-007.c, p-008.c 繰り返しをするために利用する変数(文字列が入っている)を、 繰り返す対象となる命令で利用する => まったく同じ結果の繰り返しではなく、 異なる結果を(すこしずつ変化させながら..)もたらす事が 例: p-005.c A, A, A, A, A 例: p-006.c A, A' A", A''', .. [一進法] 漢数字 一、二、三、十、百、千、万、億、兆、京、...,無量大数 数のケタ数が大きくなると、それを表現するための、 新しい漢数字が必要になる 表現したい数の種類が増えたら、(桁も増えるので..) => その種類に合わせて、漢数字の種類も増やさないといけない <=> 位取り法 0から9 までの10種類の「アラビア数字」だけで、 (有限な..)すべての数が表現できる => 「桁」 (無限)数 <=> 有限の数字 x 無限の桁(長さ) n 進数 一つ前の桁が n 個たまったら、次の桁を 1 つ増やす(繰り上がり) 普段利用しているのは、10 進数 1 と 10 の違い(表現上、桁がが違う)は、10 倍 n ( > 1 ) 進数 1 と 10 の違い n 倍 例: 2 進数の場合 10 は 1 の 2 倍になっている 「数字」は n 種類あればよい 例: 10 進数は、 0 ? 9 の 10 個の数字 2 進数は、 0 ? 1 の 2 個の数字 16 進数は、0 ? 9, A ? F までの 16 種類 # すべてが F になる 1 進数 数字は一種類、数の表現方法 : 桁数 = 数の大きさ cf. 漢数字/ローマ数字 ( => 1 進数 ) 一 I 二 II 三 III 1 進数の計算 足し算 2 + 3 5 ** *** ***** 掛け算 2 * 3 6 ** *** ****** ** *** ** ** **
課題プログラム内の「/*名前:ここ*/」の部分を書き換え「/*この部分を完成させなさい*/」の部分にプログラムを追加して、プログラムを完成させます。
Download : 20210514-01.c
/* * 20210514-01-QQQQ.c * 一進数の割り算 */ #include <stdio.h> #include <string.h> /* * prototype */ extern void one_div ( char *a, char *b ); /* * one_sub_div ( a, b, c ) * a >= b なら 1+(a-b)/c を出力、そうでなければ、何もしない (0) */ void one_sub_div ( char *a, char *b, char *c ) { if ( !strcmp ( b, "" ) ) { /* b = 0 */ /* b = 0 の時 : 1+(a-b)/c = 1+a/c */ /* +1 */ putchar ( '0' ); /* a/c */ /* ** この部分を完成させなさい */ } else if ( !strcmp ( a, "" ) ) { /* a = 0 */ /* a = 0, b > 0 の時 : (a-b)/c = 0 */ /* do nothing */ } else { /* a, b > 0 の時 : 1+(a-b)/c = 1+((a-1)-(b-1))/c */ /* ** この部分を完成させなさい */ } } /* * 一進数の割り算 */ void one_div ( char *a, char *b ) { if ( !strcmp ( b, "" ) ) { printf ( "0 で割る事はできません\n" ); } else if ( !strcmp ( a, "" ) ) { /* do nothing */ /* a/b = 0 */ } else { /* 0 ( a < b ) a/b = { 1+(a-b)/b ( a >= b ) */ one_sub_div ( a, b, b ); } } /* * main */ int main ( void ) { /* 5/2 = ? */ one_div ( "00000", "00" ); printf ( "\n" ); /* 12/3 = ? */ /* ** この部分を完成させなさい */ printf ( "\n" ); return 0; }
$ ./20210514-01-QQQQ.exe 00 0000 $
Download : 20210514-02.c
/* * 20210514-02-QQQQ.c * 一進数の階乗 */ #include <stdio.h> #include <string.h> /* * prototype */ extern void fac ( char *n ); /* * n_time_fac ( n, n1 ) * fac ( n1 ) を n 回行う * => n * fac(n1) */ void n_time_fac ( char *n, char *n1 ) { if ( !strcmp ( n, "" ) ) { /* n = 0 */ /* 何もしない */ } else { /* n > 0 */ /* ** この部分を完成させなさい */ n_time_fac ( n + 1, n1 ); /* n * fac ( n-1 ) */ } } /* * 一進数の階乗 */ void fac ( char *n ) { if ( !strcmp ( n, "" ) ) { /* n = 0 */ printf ( "0" ); /* fac(0) = 1 */ } else { /* n > 0 */ n_time_fac ( n, n + 1 ); /* fac(n) = n * fac(n-1) */ } } /* * main */ int main ( void ) { /* fac(3) */ fac ( "000" ); printf ( "\n" ); /* fac(5) */ /* ** この部分を完成させなさい */ printf ( "\n" ); return 0; }
$ ./20210514-02-QQQQ.exe 000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 $