/*
* 20200612-01-QQQQ.c
* 与えられた文字列の文字を二度ずつ出力する関数を作成する
*/
#include <stdio.h>
#include <string.h>
/*
* double_print
* 与えられた文字列(message)の文字を二度ずつ出力する
*/
void double_print ( char *message ) {
if ( !strcmp ( message, "" ) ) { /* 空文字列だった */
/* なにもする必要はない */
} 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;
}
/*
* 20200612-02-QQQQ.c
* 与えられた文字列の文字を逆順に出力する関数を作る
*/
#include <stdio.h>
#include <string.h>
/*
* reverse_print
* 与えられた文字列(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;
}
/*
"abc" => "abc"
"abc"
'a' + "bc"
putchar 再帰
=> myprint の実装
g( "abc" ) => "abc"
'a' + "bc"
putchar ( 'a' ) + g ( "bc" );
putchar ( *"abc" ) + g ( "abc" + 1 );
"abc" => "cba"
"cba"
"cb" + 'a'
再帰 putchar
f( "abc" ) => "cba"
"cb" + 'a'
f ( "bc" ) + putchar ( 'a' )
f ( "abc" + 1 ) + putchar ( *"abc" )
*/
#include <stdio.h>
int main(void) {
printf ( "Hello, World\n" );
return 0;
}
#include <stdio.h>
int main(void) {
putchar ( 'a' ); /* 画面に「文字」「a」を表示する */
return 0;
}
#include <stdio.h>
int main(void) {
printf ( "文字を出力する場合は、putchar 関数を使う\n" );
printf ( "ここに :「" );
putchar ( 'a' ); /* 画面に「文字」「a」を表示する */
printf ( "」を表示する\n" );
return 0;
}
#include <stdio.h>
int main(void) {
putchar ( 'a' ); /* 画面に「文字」「a」を表示する */
putchar ( '\n' ); /* 画面に「文字」「改行」を表示する */
/* 結果的に、画面に「a」と「改行」が表示されるので */
/* printf ( "a\n" ); と同じ結果になる */
return 0;
}
#include <stdio.h>
int main(void) {
printf ( "キーボードから、一文字入力し、さらに [Enter]キーを押してください 「" );
putchar ( getchar() );
/* キーボードから一文字入力し、その文字をそのまま出力する */
/* getchar() は、[Enter] キー(改行キー)を押すまで、待っている */
printf ( "」\n" );
return 0;
}
#include <stdio.h>
int main(void) {
printf ( "キーボードから、文字列を入力し、さらに [Enter]キーを押してください \n" );
putchar ( getchar() );
putchar ( getchar() );
putchar ( getchar() );
printf ( "\n" );
return 0;
}
#include <stdio.h>
int main(void) {
printf ( "キーボードから、文字を入力し、さらに [Enter]キーを押してください \n" );
putchar ( getchar() + 1 );
/* 入力された『文字』の次の『文字』が表示される */
printf ( "\n" );
return 0;
}
#include <stdio.h>
int main(void) {
printf ( "キーボードから、文字を入力し、さらに [Enter]キーを押してください \n" );
putchar ( getchar() - 1 );
/* 入力された『文字』の前の『文字』が表示される */
printf ( "\n" );
return 0;
}
#include <stdio.h>
int main(void) {
putchar ( *"abc" ); /* 画面に「文字」「a」を表示する */
/* 『文字列』「"abc"」に「*」つけてると、
『文字列』の先頭の『文字』である
「'a'」が得られる */
return 0;
}
#include <stdio.h>
int main(void) {
putchar ( *"abc" ); /* 先頭の文字「a」 */
putchar ( * ( "abc" + 1 ) ); /* 二番目の文字「b」 */
putchar ( * ( "abc" + 2 ) ); /* 二番目の文字「c」 */
/* "abc" + 2 => ( "abc" + 1 ) + 1 */
/* => "bc" + 1 */
/* => "c" */
printf ( "\n" );
return 0;
}
#include <stdio.h>
int main(void) {
putchar ( "abc"[0] ); /* 先頭(0番目)の文字「a」 */
putchar ( "abc"[1] ); /* 二番目の文字「b」 */
putchar ( "abc"[2] ); /* 三番目の文字「c」 */
/* "abc"[2] <-> *( "abc" + 2 ) <-> * ( "c" ) <-> 'c' */
printf ( "\n" );
return 0;
}
#include <stdio.h>
#include <string.h>
/*
* myprintf ( char *string );
* 引数で指定された『文字列』を表示する
*/
void myprintf ( char *string ) {
/*
string が空文字列の時とそうでない時に分ける
*/
if ( !strcmp ( string, "" ) ) {
/* 空文字列の時は何もしない */
} else {
putchar ( *string ); /* 先頭の『文字』を出力 */
myprintf ( string + 1 ); /* 残りの『文字列』を出力 (再帰) */
}
}
int main(void) {
myprintf ( "Hello, World\n" );
return 0;
}
#include <stdio.h>
#include <string.h>
/*
* myprintf ( char *string );
* 引数で指定された『文字列』を表示する
*/
void myprintf ( char *string ) {
/*
string が空文字列の時とそうでない時に分ける
*/
if ( *string == '\0' ) { /* 『文字列』の先頭の『文字』が「null 文字('\0')」に等しい */
/* => 『文字列』が「空文字列("")」である事と同じ */
/* C 言語で「同じ値」を意味する等式記号は「==」を使う */
/* => 「!strcmp ( string, "" )」と同じ */
/* 空文字列の時は何もしない */
} else {
putchar ( *string ); /* 先頭の『文字』を出力 */
myprintf ( string + 1 ); /* 残りの『文字列』を出力 (再帰) */
}
}
int main(void) {
myprintf ( "Hello, World\n" );
return 0;
}
前回(2020/06/05)の内容
複雑な数学パズルを、再帰を用いて解決する
順接/条件分岐/再帰
=> 万能性
原理的に解く事ができるものは、解ける
解くプログラムを作成する事ができる
再帰的に問題を解くパズル
ハノイの塔
高さ N のハノイの塔の問題を解くには
高さ N-1 のハノイ塔を移動し
サイズ N の円盤を移動
高さ N-1 のハノイ塔を移動
=> 問題をより簡単な問題に分割
再帰 => 数学的に「正しく問題を解く」事が証明できる
プログラムが動いたから OK だけでなく
=> ある特殊な条件ではうまく行く事が保証される
作成されたプログラムが、「任意の場合」でもうまくゆく事を
証明する事ができる
=> 数学の能力が要求される
砂漠の旅人
「数学」と「情報」の関係
プログラムを作成して問題を解く
「アルゴリズム」:問題を解くための手順
数学的な「ものの考え方」
問題を解く鍵(アルゴリズム)になる
数学の概念でアルゴリズムにつながる考え方
計算
色々モデル(数学概念:線形空間/収束/分類/帰納法/背理法/etc..)
C 言語における『文字』
cf.
『文字列』: C 言語の表現上の「文字列」を『文字列』であらわす
「"」で挟さまれた「文字」の並びで表現
例 : 「"abc"」は、三つの「文字」「a」,「b」,「c」からなる「文字列」を表す
=> 『文字列』は、長さをもっており、この例では長さ 3 になる
『文字』: C 言語の表現上の「文字」
「'」で挟まれた、一つの「文字」で表現
例 : 「'a'」は、一つの「文字」「a」を表す
『文字』は、長さはない ( 常に 1 と考えてもよいが、意味がない )
注意 : 「"a"」は、長さ 1 の『文字列』で、「'a'」は『文字』なので、異なるもの
C 言語では、『文字』と『文字列』は、まったく違うもの
文字の入力と出力
『文字』の出力
cf.
『文字列』の出力
printf 関数を使う
例: printf ( "abc\n" );
=> 画面に「abc」と改行が表示される
エスケープシーケンス : 「\n」は、
「改行」一文字を表す
putchar ( 'a' );
=> 画面に対して、「文字」「a」を出力する
# 同じ「出力(画面への表示)」機能
# 出力対象(『文字』と『文字列』)が異なる
# => 異なる関数(putchar,printf)を利用する必要がある
文字の表現
文字は「'」で挟む (cf. 「文字列」は「"」で挟む)
当分は、半角のみ、日本語の「文字」は扱わない
半角 : 英数、大文字小文字、簡単な記号
=> ASCII Code 表の対象
# 実は、C 言語では、「全角の文字」(日本語の漢字)も扱える
# 色々な制約 : この講義では扱ない
# !! 文字コードによって結果異なる
# !! Windows は SJIS / Ubuntu は UTF-8
# !! => Windows と Ubuntu で結果が違う
## 「全角文字を含む文字列」は「表示目的」で利用する
文字の出力
putchar( 文字 ); を使う
「putchar ( 'a' );」 で文字('a')が出力される
改行文字は '\n' で表す : putchar ( '\n' ) で改行する
エスケープシーケンスは、「文字」の表現にも利用される
「'\n'」は、「一文字の改行」を表す『文字』を表している
文字の「入力」
「出力」=> 画面になにを表示する機能
「入力」=> キーボードからキーを押して、そのキーに対応する『文字』を
プログラム内で扱えるようにする機能
「getchar()」とすると、キーボードからの入力を待つ
キーボードからキーを押して、さらに[Enter]キーを押すと
そのキーに対応する『文字』とさらに'\n' が入力される
[プログラム]
printf ( "キーボードから、一文字入力し、さらに [Enter]キーを押してください 「" );
^^^^^^^^^^^^^^^^^^^^^^^^^^(1)^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
putchar ( getchar() );
========= キーボーから入力 : 入力内容がエコーバック
~
printf ( "」\n" );
++++++++++++++++++
[出力]
キーボードから、一文字入力し、さらに [Enter]キーを押してください 「a
^^^^^^^^^^^^^^^^^^^(1)^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^=
= : 「a」と「改行」が表示 : キーボードからの入力 ('a'+[Enter])
a」
~ : 「a」という文字が表示されている : getchar() で入力された『文字』を出力
++ : 閉じかっこと改行が表示
getchar() によって、「プログラムの実行時」に、
キーボードから入力された『文字』をプログラム内で利用できる
[プログラム]
printf ( "キーボードから、文字列を入力し、さらに [Enter]キーを押してください \n" );
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
それぞれ、一文字入力し、出力をする
putchar ( getchar() );
ここで、初めて入力待ちになり、
キーボードから「"abc\n"」が入力される
最初の文字「'a'」が getchar でとりだされ、出力
=> 'a' のみ出力
putchar ( getchar() );
getchar() が呼ばれて、「入力」が要求されるが、
すでに、"abc\n" が入力済で、かつ、そのうちの
先頭の一文字「'a'」は、一つ前の getchar でとりだされているので、
ここでは、次の「'b'」が取り出され、それが表示される
=> 'b' のみ出力
putchar ( getchar() );
=> 'c' のみ出力
!!! ここで、まだ、「改行」が残ったまま、食い残しが残っている
printf ( "\n" );
=> 改行
[出力]
キーボードから、文字列を入力し、さらに [Enter]キーを押してください
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
abc
==== : 文字列「"abc\n"」: 改行も含め 4 文字
abc
「putchar ( getchar() );」とすると、入力した文字が出力される
文字の計算
cf.
『文字列』には +1 する事ができた
=> 先頭の一文字が失われ、長さが 1 短い文字列になる
例 : "abc" + 1 => "bc"
『文字』 + 1
=> 次の『文字』になる
'a' + 1 => 'b'
'A' + 1 => 'B'
'1' + 1 => '2' !!! '1' + '1' は '2' にならない
!!! 1 + 1 も '2' ではない ( 2 )
!!! 「1」と「'1'」は違う物
!!! 「数値」と「数字」
!!! 「数字」 => '0' ? '9'
!!! 「数値」 => 無限に存在する
じゃ、
'z' + 1
'9' + 1
は... ?
『文字』の「次」は
「ASCII コード表順」の次になるので
'z' + 1 => '{'
となる。
逆に、『文字』に「-1」する事により(ASCII コード表順の) 前の『文字』になる
==
C 言語では、『文字』が扱える
プログラム中で表現ができる : 'a' で、「文字」「a」を表す事ができる
出力ができる : puthar ( 'a' ) で「a」が画面に表示される
入力ができる : getchar() でキーボードから「文字」が入力できる
計算ができる : +1 で次の『文字』、-1 で前の『文字』になる
!!! 『文字』と『文字列』は別物 ( "a" と 'a' )
『文字』と『文字列』の関係
『文字列』「"abc"」は、三つの「文字」「a」,「b」,「c」からなる
=> 長さは 3 になる
「文字」「a」は、C 言語では、「'a'」なので、この関係がどうなっているか
『文字列』から、その一部である『文字』を取り出す方法
# 『文字列』を『文字』に分解する方法
# !!! 分解 => 合成もあるが、二周目
「*」演算子 ( ポインター剥ぎ / 間接参照演算子 )
『文字列』の先頭に「*」を付けると、「先頭の『文字』」が取り出せる
例 : *"abc" => 'a'
=> 先頭以外の文字は ??
『文字列』「"abc"」の二文字目: 『文字』「'b'」
!!! 『文字列』の計算
!!! "abc" +1 => "bc"
=> * ( "abc" + 1 )
「[]」演算子
*( X + i ) <-> X[i]
X : 何か ( 文字列 )
i : 整数値
例:
*"abc" <-> *( "abc" + 0 ) <-> "abc"[0] <-> 'a'
*( "abc" + 1 ) <-> "abc"[1] <-> 'b'
*( "abc" + 2 ) <-> "abc"[2] <-> 'c'
「空文字列」へ「*」をつけるとどうなるか ?
=> 結果は、「null 文字」「'\0'」になる
*"" => '\0'
!!! 空文字列は「""」で、「null 文字」は「'\0'」の事
!!! => 全然違う
イメージとして、
"abc" は、{ 'a', 'b', 'c', '\0' }となるもの
!!! 『文字列』の最後には必ず「null 文字」がいる
!!! => 「null 文字」が『文字列』の終わりを表す
課題プログラム内の「/*名前:ここ*/」の部分を書き換え「/*この部分を完成させなさい*/」の部分にプログラムを追加して、プログラムを完成させます。
Download : 20200612-01.c
/*
* 20200612-01-QQQQ.c
* 与えられた文字列の文字を二度ずつ出力する関数を作成する
*/
#include <stdio.h>
#include <strings.h>
/*
* double_print
* 与えられた文字列(message)の文字を二度ずつ出力する
*/
void double_print ( char *message ) {
if ( !strcmp ( message, "" ) ) { /* 空文字列だった */
/* なにもする必要はない */
} else {
putchar ( *message ); /* 取り敢えず、一つ分は出す */
/*
** この部分を完成させなさい
*/
double_print ( message + 1 ); /* 再帰呼出しをする */
}
}
/*
* main
*/
int main ( void ) {
double_print ( "abc" );
printf ( "\n" );
double_print ( "1234567" );
printf ( "\n" );
return 0;
}
$ ./20200612-01-QQQQ.exe aabbcc 11223344556677 $
Download : 20200612-02.c
/*
* 20200612-02-QQQQ.c
* 与えられた文字列の文字を逆順に出力する関数を作る
*/
#include <stdio.h>
#include <strings.h>
/*
* reverse_print
* 与えられた文字列(message)の文字を逆順に出力する
*/
void reverse_print ( char *message ) {
if ( !strcmp ( message, "" ) ) { /* 空文字列だった */
/* なにもする必要はない */
} else {
/* ここで再帰呼出しを行うのだが... */
/*
** この部分を完成させなさい
*/
}
}
/*
* main
*/
int main ( void ) {
reverse_print ( "abc" );
printf ( "\n" );
reverse_print ( "1234567" );
printf ( "\n" );
return 0;
}
$ ./20200612-02-QQQQ.exe cba 7654321 $