/* * 20200515-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 ) { up ( edge + 1 ); /* down ( edge ); */ printf ( edge ); printf ( "\n" ); down ( edge + 1 ); } /* * main */ int main ( void ) { pyramid ( "*****" ); printf ( "\n" ); pyramid ( "**********" ); return 0; } /* [目的] pyramid ( "*****" ); p(S) => u("****"); printf ( "*****" ); printf ( "\n" ); d("****"); => u("*****"+1); u(S+1) printf ( "*****" ); printf ( "\n" ); d("*****"+1); d(S) => * ** *** **** u() ------- ***** printf ------- **** d() *** ** * */
/* * 20200522-01-QQQQ.c * 一つ目の引数の文字列の長さの個数だけ、 * 二つ目の引数の文字列を出力する関数の作成 */ #include <stdio.h> #include <string.h> /* strcmp を利用するために必要 */ /* * n_times_print_string ( char *times, char *string ) * times : 繰返し回数を表す文字列 * string : 出力する文字列 */ 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 回数 */ /* ** この部分を完成させなさい */ return 0; }
#include <stdio.h> int main(void) { printf ( "Hello, World\n" ); /* 「"Hello, World\n"」が 「Hello, World\n」という内容(文字の列)を示す 「H」,「e」, ... , 「\n」という、 「文字」が 13 文字からなる文字の並び 文字列に含まれる文字の個数を文字の長さ => 内容(値) 「文字列」 => 種類(型): C 言語で扱えるデータの種類 */ /* 誤りの例: printf 関数の引数が「文字列」でない printf ( Hello, World\n ); */ return 0; }
#include <stdio.h> int main(void) { printf ( "\n\n\n\n" ); /* 「\n」一つで改行 1 回 (計 4 回改行) */ printf ( "\"abc\"\n" ); /* 「"abc"」+ 改行 */ printf ( "「\\」を出すには「\\\\」とする必要がある。\n" ); /* 文字列の中に「日本語」を入れる事ができるが */ /* ソフトウェア概論での「文字」や「文字列」には、 日本語が含まれていない場合のみを考える */ /* 画面の表示で、日本語を使いたい場合にのみ、 日本語を含む文字列を利用する */ /* [コメント] 日本語を含む文字列も、当然 C 言語で扱える ところが、"日本語" の長さが何なるかは、 文字コードによって異なるので、 同じプログラムでも、文字コードによって 異なる振る舞いをする ( ややっこしい ) この講義では文字コードは UTF-8 と限定する => unix の標準 <= windows は UTF-8 でなく SJIS の事が多い */ return 0; }
#include <stdio.h> #include <string.h> /* * 引数である string が特別な値( "sv" ) の時だけ、 * 特別な事 ( "Hello" を出力 ) をする * それ以外の時には、「引数の値」をそのまま出力する */ void func ( char *string ) { if ( !strcmp ( string, "sv" ) ) { /* 「!strcmp ( A, B )」(ただし、A,B は文字列) は A, B が同じ時に「真(条件成立)」 そうでないときに「偽(条件不成立)」になる */ /* if ( 条件式 ) { X } else { Y } 条件式が真(条件が成立)の時に X の命令を そうでないときは Y の命令を実行する [if 構文] */ printf ( "Hello" ); /* 関数呼び出し(文) */ /* if ( "abc" ); <= 誤り */ } else { printf ( string ); } } int main(void) { func ( "This" ); /* 引数が特別値 「"sv"」でないので */ /* 引数の値で示された文字の列「This」を出力 */ printf ( "\n" ); func ( "sv" ); /* 引数が特別値 「"sv"」なので */ /* 引数の値と関係ない文字の列「Hello」を出力 */ printf ( "\n" ); func ( "That" ); /* 引数が特別値 「"sv"」でないので */ /* 引数の値で示された文字の列「That」を出力 */ printf ( "\n" ); return 0; }
#include <stdio.h> #include <string.h> /* * 引数である string が 1) "abc" => 「ABC」を出力 2) "XYZ" => 「xyz」を出力 3) その他 => そのまま ( "abc" でも "XYZ" でもない ) => 1) と、「 2) と 3) 」の二つに分ける 1) "abc" の時 2), 3) "abc" でないとき */ void func ( char *string ) { if ( !strcmp ( string, "abc" ) ) { /* 1) "abc" の時 */ printf ( "ABC" ); } else { /* 2), 3) */ /* 2) と 3) の区別を行う */ if ( !strcmp ( string, "XYZ" ) ) { /* 2) */ printf ( "xyz" ); } else { /* 3) */ printf ( string ); } } } int main(void) { func ( "abc" ); printf ( "\n" ); func ( "XYZ" ); printf ( "\n" ); func ( "pQr" ); printf ( "\n" ); return 0; }
#include <stdio.h> #include <string.h> /* * 引数である string が 1) "abc" => 「ABC」を出力 2) "XYZ" => 「xyz」を出力 3) その他 => そのまま ( "abc" でも "XYZ" でもない ) => 1) と、「 2) と 3) 」の二つに分ける 1) "abc" の時 2), 3) "abc" でないとき */ /* インデント(字下げ)がない書き方 */ /* インデントはあってもなくても、プログラムの機能には影響しない */ /* しかし、 正しいインデントがなされていると、「読みやすい */ void func ( char *string ) { if ( !strcmp ( string, "abc" ) ) { /* 1) "abc" の時 */ printf ( "ABC" ); } else { /* 2), 3) */ /* 2) と 3) の区別を行う */ if ( !strcmp ( string, "XYZ" ) ) { /* 2) */ printf ( "xyz" ); } else { /* 3) */ printf ( string ); } } } int main(void) { func ( "abc" ); printf ( "\n" ); func ( "XYZ" ); printf ( "\n" ); func ( "pQr" ); printf ( "\n" ); return 0; }
#include <stdio.h> #include <string.h> /* */ void func ( char *string ) { if ( !strcmp ( string, "abc" ) ) { printf ( "ABC" ); /* 1) */ } else { if ( !strcmp ( string, "XYZ" ) ) { printf ( "xyz" ); /* 2) */ } else { if ( !strcmp ( string, "lmn" ) ) { printf ( "ZZZ" ); /* 3) */ } else { printf ( string ); /* 4) */ } } } } int main(void) { func ( "abc" ); printf ( "\n" ); func ( "XYZ" ); printf ( "\n" ); func ( "lmn" ); printf ( "\n" ); func ( "pQr" ); printf ( "\n" ); return 0; }
#include <stdio.h> #include <string.h> /* */ void func ( char *string ) { if ( !strcmp ( string, "abc" ) ) { printf ( "ABC" ); /* 1) */ } else if ( !strcmp ( string, "XYZ" ) ) { printf ( "xyz" ); /* 2) */ } else if ( !strcmp ( string, "lmn" ) ) { printf ( "ZZZ" ); /* 3) */ } else { printf ( string ); /* 4) */ } } int main(void) { func ( "abc" ); printf ( "\n" ); func ( "XYZ" ); printf ( "\n" ); func ( "lmn" ); printf ( "\n" ); func ( "pQr" ); printf ( "\n" ); return 0; }
#include <stdio.h> int main(void) { printf ( "abc\n" ); /* 画面に「abc」+「改行」を表示する */ printf ( "abc\n" + 1 ); /* 文字列に +1 すると、 => 先頭の文字が欠けて、一文字短くなった文字列になる */ /* 「"abc\n" + 1」=>「"bc\n"」 */ printf ( "abc\n" + 2 ); /* 「"abc\n" + 2」=> 「"abc\n" + 1 + 1」 */ /* => 「"bc\n" + 1」 */ /* => 「"c\n"」 */ /* 長さ N の文字列に整数値 N を加えると 長さが 0 の文字列「""(空文字列)」になる */ /* 長さが 0 より長い文字列は、+1 して、 長さが 1 だけ短い文字列にすることでき、 これを繰り返すと、長さ 0 にいつかはなる */ return 0; }
#include <stdio.h> void func(void) { printf ( "Hello, World\n" ); /* 関数 printf を呼ぶ */ /* 関数の定義の本体の中で、関数が呼べる */ /* 関数 A を関数 B (の呼び出し)で定義 */ /* 関数 B の呼び出しに対し、A という名前を付ける */ } int main(void) { func(); /* 関数呼び出し */ return 0; }
#include <stdio.h> void func(void) { printf ( "Hello, World\n" ); /* 関数 printf を呼ぶ */ func(); /* 自分自身(関数func の定義の中で) 関数 func を呼び出し */ } int main(void) { func(); /* 関数呼び出し */ return 0; }
#include <stdio.h> #include <string.h> /* 有限回 繰り返す 繰り返すには、「再帰呼び出し」を使う 無条件だと「無限ループ」になる 条件をつけて、 条件が成立した(もう十分) 再帰呼び出しをしない(関数が有限回の命令の実行で終了する) 条件が成立しな(まだ、必要分がおわっていない)かったら 再帰呼び出し(繰り返す) => if 構文 => 関数に引数 引数に文字列(ntimes)を与えて、 文字列の長さの回数だけ、"Hello, World\n"を出力する */ void func( char *ntimes ) { if ( !strcmp ( ntimes, "" ) ) { /* ntimes が "" (空文字列) と同じ */ /* => 文字列の長さが 0 */ /* => なにもしなくてよい */ /* ここではなにもしなくて(関数を終了) */ } else { /* 指定された文字列の長さは 0 より長い */ printf ( "Hello, World\n" ); func ( ntimes + 1 ); /* さらに、繰り返したいので、「再帰」呼び出し */ /* 再帰呼び出しの引数文字列の長さは、 すでに、一行書いているので、 元の長さより、1 だけ短くなってほしい */ /* 文字列の長さを 1 減らすには、 +1 すればよい */ } } int main(void) { printf ( "\n" ); func( "" ); printf ( "123\n" ); func( "123" ); /* 長さ 3 の文字列なので、3 行出力 */ printf ( "123456789\n" ); func( "123456789" ); /* 長さ 9 の文字列なので、3 行出力 */ return 0; }
#include <stdio.h> #include <string.h> /* イデオム(成形句) 一つ目の引数文字列の長さの回数だけ、命令 C() 実行する関数は、 次の関数 fC() を定義するだけ void fC(char *ntimes) { if ( !strcmp ( ntimes, "" ) ) { なにもしない } else { C(); C を一度実行 fC( ntimes + 1 ); 再帰呼び出し } } */ void func( char *ntimes ) { if ( !strcmp ( ntimes, "" ) ) { } else { printf ( "Hello, World\n" ); /* ここが ntime の長さだけ実行される */ func ( ntimes + 1 ); } } int main(void) { printf ( "\n" ); func( "" ); printf ( "123\n" ); func( "123" ); /* 長さ 3 の文字列なので、3 行出力 */ printf ( "123456789\n" ); func( "123456789" ); /* 長さ 9 の文字列なので、3 行出力 */ return 0; }
#include <stdio.h> #include <string.h> void func( char *ntimes ) { if ( !strcmp ( ntimes, "" ) ) { } else { printf ( ntimes ); printf ( "\n" ); func ( ntimes + 1 ); } } int main(void) { printf ( "\n" ); func( "" ); printf ( "123\n" ); func( "123" ); /* 長さ 3 の文字列なので、3 行出力 */ /* func ( "123" ); => // ntimes = "123" if ( !strcmp ( ntimes, "" ) ) { } else { printf ( ntimes ); printf ( "\n" ); func ( ntimes + 1 ); } => if ( !strcmp ( "123", "" ) ) { } else { printf ( "123" ); printf ( "\n" ); func ( "123" + 1 ); } => printf ( "123" ); printf ( "\n" ); func ( "23" ); => printf ( "123" ); printf ( "\n" ); // ntimes = "23" if ( !strcmp ( ntimes, "" ) ) { } else { printf ( ntimes ); printf ( "\n" ); func ( ntimes + 1 ); } => printf ( "123" ); printf ( "\n" ); printf ( "23" ); printf ( "\n" ); func ( "1" ); => printf ( "123" ); printf ( "\n" ); printf ( "23" ); printf ( "\n" ); printf ( "3" ); printf ( "\n" ); func ( "" ); => printf ( "123" ); printf ( "\n" ); printf ( "23" ); printf ( "\n" ); printf ( "3" ); printf ( "\n" ); // ntimes = "" if ( !strcmp ( ntimes, "" ) ) { } else { printf ( ntimes ); printf ( "\n" ); func ( ntimes + 1 ); } => printf ( "123" ); printf ( "\n" ); printf ( "23" ); printf ( "\n" ); printf ( "3" ); printf ( "\n" ); => 123 23 3 */ printf ( "123456789\n" ); func( "123456789" ); /* 長さ 9 の文字列なので、3 行出力 */ return 0; }
#include <stdio.h> #include <string.h> void func( char *ntimes ) { if ( !strcmp ( ntimes, "" ) ) { } else { func ( ntimes + 1 ); printf ( ntimes ); printf ( "\n" ); } } int main(void) { printf ( "\n" ); func( "" ); printf ( "123\n" ); func( "123" ); /* 長さ 3 の文字列なので、3 行出力 */ /* func ( "123" ); => // ntimes = "123" if ( !strcmp ( ntimes, "" ) ) { } else { printf ( ntimes ); printf ( "\n" ); func ( ntimes + 1 ); } => if ( !strcmp ( "123", "" ) ) { } else { printf ( "123" ); printf ( "\n" ); func ( "123" + 1 ); } => printf ( "123" ); printf ( "\n" ); func ( "23" ); => printf ( "123" ); printf ( "\n" ); // ntimes = "23" if ( !strcmp ( ntimes, "" ) ) { } else { printf ( ntimes ); printf ( "\n" ); func ( ntimes + 1 ); } => printf ( "123" ); printf ( "\n" ); printf ( "23" ); printf ( "\n" ); func ( "1" ); => printf ( "123" ); printf ( "\n" ); printf ( "23" ); printf ( "\n" ); printf ( "3" ); printf ( "\n" ); func ( "" ); => printf ( "123" ); printf ( "\n" ); printf ( "23" ); printf ( "\n" ); printf ( "3" ); printf ( "\n" ); // ntimes = "" if ( !strcmp ( ntimes, "" ) ) { } else { printf ( ntimes ); printf ( "\n" ); func ( ntimes + 1 ); } => printf ( "123" ); printf ( "\n" ); printf ( "23" ); printf ( "\n" ); printf ( "3" ); printf ( "\n" ); => 123 23 3 */ printf ( "123456789\n" ); func( "123456789" ); /* 長さ 9 の文字列なので、3 行出力 */ return 0; }
前回(2020/05/15)の内容 文字と文字列 「文字列」は、「"」に挟まれた、「文字」の並び 文字列に含まれる文字の個数が、文字列の長さ 「"" (空文字列)」 (文字が)何も含まれていない文字列 空文字列の長さは 0 になる 「文字」は、ASCII Code 表にある文字(半角英数字記号) 基本は、キーボードのキーを一度押すと入力されるもの キーボードの[A]キーを押すと、「a」という文字が入る 「文字」では、基本は 1 byte で、一つの「文字」を表す => ASCII Code 表に記載されている文字 エスケープシーケンス : 「\」エスケープ文字 「\」から始まる「文字の並び」で、それで一つの「文字」を表す \n => 「\」と「n」の二文字で、一つの「文字」である「改行」を表す \t => タブ \" => 「"」文字一つを表す 文字列の中に、「"」を含めるときに、 エスケープシーケンスが必要 \\ => 「\」文字一つを表す。 条件分岐 : if 構文の利用 引数の値(文字列)によって、振舞いを変更する関数を作る => if 構文を利用する 構文 : if ( 「条件式」 ) { 「条件成立時の命令」 } else { 「不成立時の命令」 } 「if 構文」を利用すると 二つの命令のどちらか一方が実行される 条件が成立しないと、やっていけない命令 => 条件判定が不可欠になる 条件式 : 「真(条件が成立)」 または 「偽(条件が不成立)」 の値を取る式 例 : 「!strcmp(A,B)」は、 文字列 A, B が 同じ時に「真」 で その他は「偽」 となる 注意 : strcmp を利用するときには、 #include <string.h> も必要 やりたい事が三つ以上あったら ? if 構文は、一つの条件を利用して、二つの場合の場合分けが可能 => 三つだったらどうするか ? if 構文を利用しないと「一つの場合」しか扱わない 「if 構文を一つ」利用すると「一つの条件」で、「二つの場合」が扱える 条件を一つ加えて if 構文を使うと、場合が一つ増える => 3 = 1 + 2 = 1 + 1 + 1 if 構文と(一緒に条件)を二つ使えばよい 例: 1) "abc" => ABC 2) "XYZ" => xyz 3) その他 => そのまま 三通り以上の場合分けをしたい場合は、 if 構文を、「入れ子」にする事により、実現可能 n + 1 通りの分類は、 n 個の if 構文(と条件)で記述可能 四通り 上に加えて "lmn" => "ZZZ" 1) "abc" => ABC 2) "XYZ" => xyz 3) "lmn" => "ZZZ" 4) その他 => そのまま if 構文が重なる(入れ子にする)ときには、 インデント(字下げ)を行い、if 構文の範囲が明確になるようにする => 複数の if 構文を組み合わせると、インデントが深くなる => 読みにくい else の中身が、一つの if 構文の場合は else if を利用して、(結果的に 「{」と「}」の組み合わせが減るので..) インデントの深さをさぼっても、わかりやすい書き方が可能になる [2020/05/22] 再帰 関数の定義の中で、自分自身を呼び出す事が可能 => 再帰呼び出し ( [数学] 帰納的定義 ) もし、 再帰呼び出しを、「無条件」に行うと、 無限ループ 「無限ループ」が目的なら、OK => 稀な例 (ゲーム機、OS etc..) => 「無限」=「必要なだけ何回でも繰り返せる」 => うまく条件を設定して、「必要なくなった」事が提示できれば、 必要なだけ繰り返す事ができる 関数定義の本体の所に自分自身を呼び出す(再帰呼び出し)によって、 同じ命令を繰り返す事ができる 繰り返し(再帰呼び出し)を途中で終了するために、 if 構文を利用して、条件判断を行う 再帰呼び出しを利用した、繰り返し 命令 C() を繰り返すには、 再帰的に定義された関数 f() を次の様に定義 void f(char *S) { if ( S に関する条件 ) { なにもしない } else { C(); -- 繰り返したい命令 f( S の値変更 ); -- 再帰呼び出し } } => S に変更を繰り返して、S に関する条件が成立するまで C() を繰り返し実行する [目的] A = 123 23 3 このために fA("123")=A となる関数 fA を定義したい A -> A1 + A2 A1 = 123 => printf ( "123" ); printf ( "\n" ); ---------------------- A2 = 23 => fA("23") = fA( "123" + 1 ) 3 fA("") => なにもしない なにもしない (S が "") fA(S) = { printf ( S ); printf ( "\n" ); fA(S + 1); void fA(char *S) { if ( !strcmp ( S, "" ) ) { } else { printf ( S ); printf ( "\n" ); fA( S + 1 ); } } == [目的] B = 3 23 123 このために fB("123")=B となる関数 fB を定義したい B -> B1 + B2 B = 3 B1 23 ---------------------- 123 B2 B1 = 3 => fB( "23" ); 23 B2 = 123 => printf ( "123" ); printf ( "\n" ); fB("") => なにもしない なにもしない (S が "") fB(S) = { fB(S + 1); printf ( S ); printf ( "\n" ); void fB(char *S) { if ( !strcmp ( S, "" ) ) { } else { fB( S + 1 ); printf ( S ); printf ( "\n" ); } }
課題プログラム内の「/*名前:ここ*/」の部分を書き換え「/*この部分を完成させなさい*/」の部分にプログラムを追加して、プログラムを完成させます。
Download : 20200522-01.c
/* * 20200522-01-QQQQ.c * 一つ目の引数の文字列の長さの個数だけ、 * 二つ目の引数の文字列を出力する関数の作成 */ #include <stdio.h> #include <string.h> /* strcmp を利用するために必要 */ /* * n_times_print_string ( char *times, char *string ) * times : 繰返し回数を表す文字列 * string : 出力する文字列 */ void ntimes_print_string ( char *ntimes, char *string ) { if ( !strcmp ( ntimes, "" ) ) { /* ntimes の長さが 0 */ /* 何もする必要はない (関数は終了) */ } else { /* そうでなければ */ /* 二つ目の文字列を出力 */ printf ( string ); /* 再帰を利用して、残りの回数だけプリント */ /* 一つ目の引数は、長さを減らす必要がある */ /* 二つ目の引数は、そのまま渡してよい */ /* ** この部分を完成させなさい */ } } /* * main */ int main ( void ) { /* "Hello, World\n" を 5 回数 */ ntimes_print_string ( "12345", "Hello, World\n" ); /* "こんにちは、皆さん\n" を 8 回数 */ /* ** この部分を完成させなさい */ return 0; }
$ ./20200522-01-QQQQ.exe Hello, World Hello, World Hello, World Hello, World Hello, World こんにちは、皆さん こんにちは、皆さん こんにちは、皆さん こんにちは、皆さん こんにちは、皆さん こんにちは、皆さん こんにちは、皆さん こんにちは、皆さん $
Download : 20200522-02.c
/* * 20200522-02-QQQQ.c * 引数の文字列の長さに対するフィボナッチ数だけの文字 "*" を出力する関数 * * 0 ( n = 0 の時 ) * fib(n) = { 1 ( n = 1 の時 ) * fib(n-1)+fib(n-2) ( その他 [n > 1 の時] ) */ #include <stdio.h> #include <string.h> /* strcmp を利用するために必要 */ /* * void c_fibnacci ( char *n ) * n : 長さで、整数値を表す文字列 */ void c_fibnacci ( char *n ) { if ( !strcmp ( n, "" ) ) { /* n の長さが 0 */ /* 何もしなくて良い (関数は終了) */ } else if ( !strcmp ( n + 1, "" ) ) { /* n の長さが 1 */ /* ** この部分を完成させなさい */ putchar ( '*' ); } else { /* その他 ( n の長さが 1 より長い ) */ /* 再帰呼出しを二度行う */ /* 一度目は、1 だけ長さを減らす */ /* ** この部分を完成させなさい */ /* ニ度目は、2 だけ長さを減らす */ c_fibnacci ( n + 2 ); } } /* * main */ int main ( void ) { /* fib(4) = 3 回だけ「*」を出す */ c_fibnacci ( "****" ); putchar ( '\n' ); /* 改行 */ /* fib(7) = 13 回だけ「*」を出す */ /* ** この部分を完成させなさい */ putchar ( '\n' ); /* 改行 */ return 0; }
$ ./20200522-02-QQQQ.exe *** ************* $