Download : sample-001.c
/* * 2019/05/31 sample-001.c */ /* * 2^N を計算する ( ただし、N は文字列の長さ、結果は 2^N の文字列 ) */ #include <stdio.h> #define EOS '\0' /* End Of String (\0) のマクロ定義 */ /* * pow2(N) : 2^N を計算 */ void pow2( char *N ) { if ( *N == EOS ) { /* N == '\0' すなわち N == "" の時 */ putchar ( '*' ); /* 2^0 = 1 なので、一個出力 */ } else { /* そうでなければ */ pow2( N + 1 ); /* 2^N == 2^(N-1) + 2^(N-1) の関係を利用 */ pow2( N + 1 ); } } /* * main 関数 */ int main ( void ) { printf ( "2^3\n" ); pow2 ( "***" ); printf ( "\n" ); printf ( "2^5\n" ); pow2 ( "*****" ); printf ( "\n" ); return 0; }
$ ./sample-001.exe 2^3 ******** 2^5 ******************************** $
/* * 20190524-01-QQQQ.c * 与えられた文字列の文字を二度ずつ出力する関数を作成する */ #include <stdio.h> #include <string.h> /* * double_print * 与えられた文字列(message)の文字を二度ずつ出力する * * double_print ( "abc" ) * => * aabbcc * => * aa bbcc * => * putchar ( 'a' ); * putchar ( 'a' ); * double_print ( "bc" ); 再帰呼び出し * => * putchar ( *"abc" ); * putchar ( *"abc" ); * double_print ( "abc" + 1 ); * => * putchar ( *message ); * putchar ( *message ); * double_print ( message + 1 ); */ void double_print ( char *message ) { if ( !strcmp ( message, "" ) ) { /* 空文字列だった */ /* *message == '\0' */ /* なにもする必要はない */ } else { putchar ( *message ); /* 取り敢えず、一つ分は出す */ putchar ( *message ); /* 二つ目も同様 */ double_print ( message + 1 ); /* 再帰呼出しをする */ } } /* * main */ int main ( void ) { double_print ( "abc" ); printf ( "\n" ); double_print ( "1234567" ); printf ( "\n" ); return 0; }
/* * 20190524-02-QQQQ.c * 与えられた文字列の文字を逆順に出力する関数を作る */ #include <stdio.h> #include <string.h> /* * reverse_print * 与えられた文字列(message)の文字を逆順に出力する reverse_print ( "abc" ) => cba => cb a => reverse_print ( "bc" ); putchar ( 'a' ); => reverse_print ( "abc" + 1 ); putchar ( *"abc" ); => reverse_print ( message + 1 ); putchar ( *message ); */ void reverse_print ( char *message ) { if ( !strcmp ( message, "" ) ) { /* 空文字列だった */ /* なにもする必要はない */ } else { /* ここで再帰呼出しを行うのだが... */ reverse_print ( message + 1 ); putchar ( *message ); } } /* * main */ int main ( void ) { reverse_print ( "abc" ); printf ( "\n" ); reverse_print ( "1234567" ); printf ( "\n" ); return 0; }
#include <stdio.h> #include <string.h> /* strcmp のために追加 */ /* 「再帰呼び出し」を利用して、命令の「繰り返し」を行う */ /* 繰り返したい内容 */ void repeat(void) { // printf ( "Hello, World\n" ); /* 繰り返したい内容をここに書く */ printf ( "こんにちは、みなさん\n" ); } /* 「再帰呼び出し」を利用して、命令の「繰り返し」を行う関数を作る ここで、繰り返しの回数は、引数で与えた文字列の長さとする */ void loop ( char *times ) { if ( !strcmp ( times, "" ) ) { /* 引数の値と空文字列の比較 */ /* なにもしない */ /* 一致しているということは、長さ 0 なので.. */ /* 再帰呼び出しの停止状態になっている */ } else { /* その場合は、繰り返す必要があるので... */ repeat(); /* 繰り返したい命令を呼び出す */ loop ( times + 1 ); /* 再帰呼び出しをする */ /* この時、引数の文字列の長さを一つ減らす */ /* 引数の長さがだんだん短くなって最後に 0 になる */ } } int main(void) { printf ( "3 回繰り返す\n" ); loop ( "***" ); printf ( "============\n" ); printf ( "5 回繰り返す\n" ); loop ( "*****" ); return 0; }
#include <stdio.h> int main(void) { putchar ( 'A' ); /* 'A' という文字を出力 : 結果として「A」が出力される */ putchar ( '\n' ); /* 「\n」は二文字で、一文字の意味 */ /* 「\」の事を「エスケープ文字」と呼ぶ */ /* C 言語では、「エスケープ文字」として「\」を利用する */ /* 「エスケープ(逃げ出す)文字」とは、 いまから、普段と違う状態になる(エスケープ)印 */ /* 本来、「n」は、アルファベットの「n」一文字を表す */ /* 例 : putchar ( 'n' ); => 「n」が表示される */ /* ところが、「\」が現れると、次の文字は、普段と異なる 別の意味となる(という約束をしている) */ /* 具体的には \n => 改行 \t => タブ \" => ダブルクォーテーション 「"」 \' => ダブルクォーテーション 「'」 \\ => 「\」 */ /* 他にも色々ある */ putchar ( ' ' ); putchar ( 'A' ); putchar ( '\n' ); putchar ( '\t' ); putchar ( 'A' ); putchar ( '\n' ); return 0; }
#include <stdio.h> int main(void) { printf ( "getchar を呼び出す前\n" ); putchar ( getchar() ); /* ここで何かが起きる */ printf ( "getchar を呼び出す後\n" ); /* 結果 getchar を呼び出す前 l.5 a l.6 の getchar に対して、「a[改行]」 agetchar を呼び出す後 l.6 の putchar で「a」でて、l.7 */ /* l.6 では、getchar() によって読みこまれた文字を、putchar で出力している */ return 0; }
#include <stdio.h> int main(void) { printf ( "getchar を呼び出す前\n" ); putchar ( getchar() ); /* get/put を三回呼び出す */ putchar ( getchar() ); putchar ( getchar() ); printf ( "getchar を呼び出す後\n" ); /* C 言語のプログラムから「入力」を要求すると、「入力」は「OS」が肩代わりをする 「OS」は、「入力が改行で終わるまで、待って、改行が入ると、プログラムに渡す」 => 「OS」は、あとで、何文字必要かわからないので、 入力されただけ記録して、あとは、プログラムの要求に従って、 1 文字ずつ、プログラムの getchar 要求に従って、答える 「a[改行]」 => a + \n => 二つの getchar ( l.6 と l.7 ) にそれぞれ 'a' と '\n' が渡される => l.7 で再び入力待ち */ /* getchar を使っていろいろな面白い事ができるが、 今回は、「入力待ち」をし「Enter」キーを押すまでプログラムを停止する ために用いる */ return 0; }
#include <stdio.h> int main(void) { printf ( "1. まってます..." ); getchar(); /* 改行の入力をまっているだけ.. */ printf ( "2. まってます..." ); getchar(); printf ( "3. まってます..." ); getchar(); printf ( "4. まってます..." ); return 0; }
#include <stdio.h> int main(void) { putchar ( *"abc" ); /* 文字列「"abc"」の前に * を付けると、先頭の文字「'a'」が得られる */ /* => putchar ( 'a' ) と同じ */ putchar ( *"123" ); /* => putchar ( '1' ) と同じ */ putchar ( *"xyz" ); /* => putchar ( 'x' ) と同じ */ return 0; }
#include <stdio.h> int main(void) { putchar ( *("abc"+1) ); /* "abc"+1 => "bc" *("abc"+1) => *"bc" => 'b' */ return 0; }
#include <stdio.h> #include <string.h> /* myprintf は、printf と同様、一つの文字列引数を与えると、 その文字列を標準出力に出力する */ void myprintf ( char *string ) { /* もちろん「printf ( string );」としてもよいが,.. */ /* せっかくなので putchar で頑張ろう.. */ /* myprintf ( "abc" ); => abc => a bc => putchar ( 'a' ); myprintf ( "bc" ); 再帰呼び出し => putchar ( *"abc" ); myprintf ( "abc" + 1 ); 再帰呼び出し ====> myprintf ( string ) putchar ( *string ): myprintf ( string + 1 ); 再帰構造の一般形 + 終了状態(なにもしないとき => 空文字列「""」の時 */ if ( !strcmp ( string, "" ) ) { /* do nothing */ } else { putchar ( *string ); myprintf ( string + 1 ); } } int main(void) { myprintf ( "Hello, World\n" ); return 0; }
#include <stdio.h> /* myprintf は、printf と同様、一つの文字列引数を与えると、 その文字列を標準出力に出力する */ void myprintf ( char *string ) { /* 文字同士が等しいかどうか判定する場合は、「==」を使う */ if ( *string == '\0' ) { /* !strcmp ( string, "" ) */ /* string が空文字列「""」の時 */ /* 「*""」:空文字列の先頭の文字 => '\0' (EOS : End Of String) になる */ /* do nothing */ } else { putchar ( *string ); myprintf ( string + 1 ); } } int main(void) { myprintf ( "Hello, World\n" ); return 0; } /* 文字列「"abc"」は、 'a', 'b', 'c', '\0' の四つの文字が並んでいる状態になっている 空文字列「""」も、その中に「\0」が一つだけ含まれている状態 */
#include <stdio.h> #include "s_walk.h" /* ミクで遊ぶために必要 */ // main関数 int main(int argc, char *argv[]) { /* miku の API s_walk_turn(); ミクの向きを 45 回転 s_walk_move(); ミクを一歩前に進める */ s_walk_turn(); /* ミクを後ろ向きにする */ s_walk_turn(); s_walk_turn(); s_walk_turn(); s_walk_move(); /* 三歩進む */ s_walk_move(); s_walk_move(); s_walk_turn(); /* ミクを前向きにする */ s_walk_turn(); s_walk_turn(); s_walk_turn(); return 0; }
[PC 関係のお知らせ] 1. HP ProBook のバッテリィ異常の問題 栗野ページ http://edu-gw2.math.cst.nihon-u.ac.jp/~kurino/ にアナウンスがあるので、個々に対応 自分で、調べて、問題があれば、メーカーに連絡して、交換してもらう 2. 新しい windows update 件 (2019 の春の大型アップデート)が始まっている => 基本、update はしてほしいが、「人柱」にはなりたくない # もしかしたら、問題があるかもしれず、安易に update すると、トラブルにあうかも.. # => すでに、問題点がある事がわかっている <= [対応] 現在は、windows update は、「自動更新」になっていて、制御できない => 「自動更新」をいったん、「無効」にしてしまうとよい (how to) 「Windows update の自動更新の無効・有効を簡単に切り替える(wub)」ソフトがある これをインストールして使う 栗野のページ => 2019 担当科目 => コンピュータ概論 => 5/28 のページ => wub.zip をダウンロード 使い方 まず、「すべて展開」(インストールはこれだけ) 新しくできたフォルダの中の wup.exe を実行 on/off の切り替えができる おすすめ 春/秋の大型の時には、事前に off にして、update しない 公開されて 1 月位経つと、安定するので、その時に on にして update する [ファイルのダウロードと展開] 本日のページから、hanoi.zip と miku.zip をダウロード ダウロード先は、今日のフォルダ(を先に作って、そこに..)保存する ダウンロードした二つの zip ファイルは、ともに、「展開」しておく [前回(2019/05/24)の内容] 「繰り返し」の復習 : 再帰呼出しする関数の定義は「繰り返し」を表現 再帰呼び出しを使って、「何か」を「繰り返す」という関数が作れる 「繰り返す」という機能は、繰り返したい、「何か」とは独立に記述できる # どのような命令でも繰り返す事ができる 命令の組み合わせ : 順接/条件分岐/繰返し、これがあれば「万能」になる 順接 命令 A, B => A;B を順番に実行する 条件分岐(if 構文) 命令 A, B と条件 Q => Q の成立時に A, 不成立時に B を実行する 再帰呼び出しを利用して定義した関数 命令 A と実行回数 N => A を N 回繰り実行する !!! N は固定(プログラム作成時に決まるの)でなくて、プログラムの実行時に定まる !!! => 有限の記述で、無限の命令の実行が可能になる 原理的に計算可能(大学院にいって、志村先生の講義を受けると習う) 当時の三者が「計算ができる」とは何かと、独立に考えた チューリングマシン ラムダカリキュラス 帰納的関数 => どれも「同じ」事しかできない (どれも同じなのだが..) どれかと同じ能力がある事を「万能の計算能力がある」と呼ぶと決めた => 順接と条件分岐と繰り返しの表現が可能なプログラミング言語は、「万能」である Turtle Gaphics (亀プログラミング) : 画面上の「亀」を制御して、図を書く # TG そのものは、単なる遊び(C 言語やって、絵も描ける..) Turtle Graphics のライブラリの利用と API の体験 API : Application Program Interface (応用プログラムを作成する時の規約) C 言語(一般にプログラミング言語)では、 基本命令を組み合わせて、プログラム(より高機能な命令:アプリケーション)を作る printf(); 文字列を出力する API => このような基本命令(として使える命令)を定めるルール 亀を操作するには、どのような命令を使えばよいか ? という規則 # C 言語を学ぶ # 基本命令となる「単語」を増やす => いろいろな API を学ぶ # 命令を組み合わせる手段を学ぶ「制御構造:順接/条件分岐/繰り返し」<= これで十分 「巨人の肩に載る」: 既存の成果を上手に再利用する API がどのように提供されるか ? # API は、「他人が作った関数群(ライブラリ)の使い方」を示したもの # => ライブラリを作った人が、(勝手に考えて)作る # cf. s_XX は、栗野が作ったので API も同様 # => OpenGL というライブラリ(API)を利用して作った => 機能を「0 から作る(車輪の再発明..)」の大変 => できるだけ、人の作ったものをそのまま、あるいは、それを組み合わせて、新しい機能を作る 「文字」の扱い : C 言語では「文字列」以外に「文字」が扱える 文字のリテラル(文字自身の表現) : 「'」+「一文字」+「'」 例 : 「'A'」という表現は、「A」という「文字」を表す cf. 「"A"」という表現は、「A」という「文字」が一つからなる「文字列」を表す !! 文字と文字列は違う(同じ操作に対する応答が異なる) !! cf. "A" に +1 すると、文字の長さが一つ減って "" (空文字) !! 'A' に +1 すると、'B' (次の文字)になる !! => 「型(type)」が異なる !! その型を持つ要素の集合や操作の種類と結果が異なる 文字の入出力 : getchar/putchar 関数で、文字が入出ができる printf の API( 暫定 : 実は、もっと色々な機能を持つ超高機能関数だが.. ) 一つの文字列引数をもち、その文字列を標準出力(通常は画面)に出力する putchar の API 一つの文字引数を持ち、その文字を標準出力(通常は画面)に出力する getchar の API 標準入力(通常はキーボード)から一文字入力して値として返す 入力する文字は、「改行キー」が押されるまで待つ(入力待ちする) 「改行キー」そのものも、入力の一部となる !! 「入力」=> プログラムの実行時に情報を与えて、その情報に基づいて、振る舞いが変えられる == 前回「文字」を学んだ 「文字列」と「文字」は違うもの 「文字列」は複数(0個以上)の「文字」が並んでいて、最後に特別な文字「\0」(NUL 文字/EOS)がある 文字列の前に「*」を付けると、先頭の文字が得られる。 すでに学んでいる 「文字列に +1 すると、文字列の先頭の文字が欠けて、文字の長さが短くなる事」を 利用すると、先頭以外の文字も取り出せる 「再帰」と「文字と文字列の関係」を利用とすると、いろいろな事ができる 特に 繰り返す二つのパターン 1. => 文字列を先頭の方から順に扱う 繰り返し命令 再帰 2. => 文字列を後ろの方から逆順に扱う 再帰 繰り返し命令 ミクの遊び方 先頭に #include "s_walk.h" miku の API s_walk_turn(); ミクの向きを 45 回転 s_walk_move(); ミクを一歩前に進める プログラムを作成して、 miki のフォルダに保存 コンパイル実行 make BASE=ファイルのベース名 test # 「ファイルのベース名」は、ソースコードファイル(例:foobar.c) # の拡張子(.c)を取り除いたもの(例:foobar) # 例: # プログラムソースファイル名が foobar.c なら # make BASE=foobar test # とする hanoi 中に libSK.a というファイルがあるが、これが邪魔をして、うまくゆかない => libSK.a を削除すればよい
課題プログラム内の「/*名前:ここ*/」の部分を書き換え「/*この部分を完成させなさい*/」の部分にプログラムを追加して、プログラムを完成させます。
Download : 20190531-01.c
/* * 20190531-01-QQQQ.c * 高さ3 のハノイの塔を手動で解く */ #include <stdio.h> /* * ハノイプログラムには、s_hanoi.h が必要 */ #include "s_hanoi.h" /* * ハノイの塔 プログラム */ int main ( void ) { /* * 最初は "1" に全ての円盤が置いてある * これを "2" に全ての円盤を移動する */ /* * ハノイで、できること * s_hanoi_init() : ハノイプログラムの開始 : 最初に一度だけ呼び出す * s_hanoi_size ( char *discs ) : ハノイの塔の高さを設定する * s_hanoi_move ( char *from, char *to ) : from にある円盤を to に移す * s_hanoi_clear () : 最初の状態に戻す * s_hanoi_stop() : ハノイ塔プログラムの終了 : return 0 の前に呼び出す */ s_hanoi_init(); /* ハノイの塔のプログラムの初期変化 */ /* s_hanoi_set ( char *discs ) を呼ばなければ、高さは 3 */ printf ( "これから解答を開始します。[Enter] キーを押してください\n" ); putchar ( '>' ); putchar ( getchar() ); /* 開始前に、一旦停止 */ /* 解答開始 */ /* 高さが、具体的な小さい数(3)で指定されているので、 解答手順を実際に書き下すだけ */ s_hanoi_move ( "1", "2" ); /* 1 から 2 に、(大きさ 1 の)円盤を移動 */ s_hanoi_move ( "1", "3" ); /* 1 から 3 に、(大きさ 2 の)円盤を移動 */ /* ** この部分を完成させなさい */ s_hanoi_move ( "1", "2" ); /* 1 から 2 に、(大きさ 3 の)円盤を移動 */ s_hanoi_move ( "3", "1" ); /* 3 から 1 に、(大きさ 1 の)円盤を移動 */ /* ** この部分を完成させなさい */ s_hanoi_move ( "1", "2" ); /* 1 から 2 に、(大きさ 1 の)円盤を移動 */ /* 解答終了 */ printf ( "プログラムを終了するには [Enter] キーを押してください\n" ); putchar ( '>' ); /* 終了前に確認 */ putchar ( getchar() ); /* ハノイの終了 */ s_hanoi_stop(); return 0; }
$ ./20190531-01-QQQQ.exe $