/*
* 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 $