前回(2020/06/19)の内容 「Hello, World」再び 最も単純なプログラム : Hello, World 差分プログラミング すでに動作がわかっていて、正しく動作するものから始める 少しずつ修正を加えながら改良して行く => 間違いの影響を小さくして、効率よりプログラム作成ができる 「車輪の発明」 => すでに、発明済の「車輪」を再度、苦労して発明しようとしている 無駄な行為 プログラムを作成する場合 すでにあるものは、そのまま「再利用」する => 既存のプログラム改良 「Hello, World」の謎 御呪い: 「#include」 #include => printf の宣言がはいっていて、これを利用している #include の機能そのものは、 /usr/include/stdio.h を読み込む main 関数宣言 => C 言語で記述された実行プログラムには、 必ず、main 関数が一つだけ含まれている必要がある ソースコードファイルに、関数の定義をする事は可能だが、 その中に、main 関数がないとダメ 二周目なので、一周目ではおこなわない、色々な説明を追加 「int」/「return 0;」 は何をしている ?? => すこし、この内容触れる予定 [2020/06/26] 「関数」という考え方 関数の定義とは(What) ? 「プログラムの一部」に「名前」を付ける事 「名前」を「関数名」と呼ぶ 「プログラムの断片」を「関数の本体」と呼ぶ 関数をどうやって利用する(How to) ? 「関数名」を指定するだけで「関数本体」が実行される (関数呼出し) 関数を定義する理由は (Why) ? 「プログラムの断片」に「名前」が付けられるので、分かり易い もちろん、「断片の内容に対応した分り易い名前をつければ..」だが.. 「関数の名前」を指定するだけで「関数本体」が実行される 何度も同じ事をする場合に便利(プログラムが短くなる) # もとの命令より、関数名が短い場合 # 関数の本体には、複数の命令がかけるので、通常は、短くなる 「引数」を利用する事により 「色々な断片」を「一つの関数本体」にまとめられる 何度も「似たような事」をする場合に便利(プログラムが短くなる) 関数の定義の「使いまわし」が可能になる 「使いまわせる」 => 色々な所で利用できる ( 違いを実引数で補う ) 「汎用」になっている <= (一部が..) 抽象化されている 「抽象化」されている所「変数」の部分 一箇所の「関数本体」を直すだけで、多数の場所の命令を直す効果がある 関数呼び出しは、同一の関数定義の本体を呼び出す 同じ名前の関数呼び出しが行われるたびに、同じ関数の本体が呼び出される => 関数の定義を変更した影響は、関数呼び出しの個数の影響になる 同じ事をするときに、 「コピペ」を利用する事ができる ここで、「もしコピーするまえの内容に誤りがあった」ならどうなるか ? コピペの結果、「誤り」もコピーされてしまう... 「コピペ」がバグの増殖を促す 「コピペ」よりよい、「関数呼び出し」という方法 => コピーしないので、「誤り」が増えない 関数」の表現方法 (復習) 関数定義(の文法) 「関数定義」は、「関数頭部」と「関数本体」に分けられる 例: 関数定義 void print_hello ( void ) { printf ( "Hello, World\n" ); } 関数頭部 : void print_hello ( void ) 関数本体 : {printf ( "Hello, World\n" );} 「関数頭部」は、「関数宣言」「関数名」「仮引数宣言」に分けられる 例: 関数頭部 void print_hello ( void ) 「関数宣言」は、void(これまで)/int(main だけ) 「関数名」は、自由に决めて良い(他と重複すると駄目だが..) print_hello 「仮引数宣言」は、「(」+「仮引数宣言並び」+「)」 ( void ) : 引数が無い場合 ( char *You ) : 引数が一つ ( 仮引数変数 You ) 「仮引数宣言並び」は、 「void」か、 「char *仮引数変数名」のカンマ(,)区切 「関数本体」は、「{」+「命令列」+「}」 関数本体 : {printf ( "Hello, World\n" );} 関数呼出し(の文法) 「関数呼出し」は、「関数名」+ 「実引数並び」 例: print_hello_you( "World" ); 関数名:print_hello_you 実引数並び: "World" 「実引数並び」は、 '()' : 引数が無い場合 か、 '(' + 「式」のカンマ並び + ')' 引数の個数だけ、並べる => それぞれ、その順に、仮引数変数と対応付けられる [文字を引数に持つ関数と型宣言] これまでの関数 引数が「無い」か、「文字列」を引数としていた 無い => void 文字列 => char *仮引数変数名 「char *」をお呪いとし、 関数を呼び出す時に、実引数として、「文字列」を指定 仮引数変数には(実引数で指定した)文字列が入っているとして、考える 例 : p-006.c の関数 print_hello_you 関数定義の頭部 void print_hello_you ( char *You ) 関数呼び出し print_hello_you( "World" ); 仮引数変数 You に実引数の文字列 "World" が入る !!! 実は、「char *」は、「文字列」に対応している !!! <<注意>> 「文字列」の時には、「char *」書くが、 !!! 「char *」は「文字列」を表しているわけではない !!! 当分は、「表していると『誤解』していて」もよい 「これまで」 引数として、文字列しか扱わなかった => そのために必要な「char *」は、「いつも同じもの」でよいので、 意味を説明する必要がない cf. #include => おまじない、いつもいれる ところが #include が増えた => strcmp を利用するため <= stdio.h は ??? => printf を利用するため (『文字列』ではなく)『文字』を引数に持つ関数の場合 例 : putchar() : 引数は『文字』である 引数宣言に(「char *」ではなく)「char」とする必要がある 仮引数変数宣言に現れる 「char *」/「char」は実は、「引数の型」を表現していた 型宣言 「char *」は『文字列』が入る仮引数変数の宣言に利用する 「char」は『文字』が入る仮引数変数の宣言に利用する 関数の引数は、 その型 ( char 型 : 値が『文字』 / char * 型 : 値が『文字列』 ) が 関数の仮引数変数の宣言の時点で定まっている => 関数呼び出しの実引数では、その型に沿った値を指定する必要がある 関数の仮引数変数に、 その型と異る(実引数で指定した)値を入れようとすると「エラー」になる => 適切な結果にならない 「型」と「演算」 型 : 値の集合と、その値を利用した演算の組(数学) 例 : n 次元実ベクトル線形空間 値の集合 : n 次元実ベクトル集合 演算 : 「定数倍」と、「和」 # 「n 次元実ベクトル集合」 # = { (x1,x2,..,xn) | xi \in R } # => 集合だけでは、「空間」にならない # 「(線形)空間」にするには # 「(線形)空間」固有の演算(線形空間の場合は、定数倍と和) # と一緒にする必要がある 『文字』に 「1 を加える」と、「次の『文字』」 'A' + 1 => 'B' '3' + 1 => '4' 『文字』の集合は「ASCII コード表に記載されているもの」 # 1 byte で表現できるから、最大 256 種類 『文字列』に 「1 を加える」と、「短くなった『文字列』」 "abc" + 1 => "bc" 『文字列』の集合は 長さが有限な、文字並びなので 長さ 0 : "" => 1 通り 長さ 1 : "a" => 256 通り 長さ 2 : "ab" => 256 x 256 通り .. 長さ n : 256^n .. +) ------------------- 1 + 256 + 256^2 + .. + .. 同じ「1 を加える」という「演算」でも、「意味」が異る 「演算」と「型」は「一組」で考える必要がある # 数学の「空間」