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 $