プログラミング成長日記 #5 -久々-
こんにちは!トマトです🍅
今日もまたC言語やっていきます!
とその前に近況報告から。
最近はflutterの講座をUdemyというサイトで購入して、それに沿って一生懸命やっていました。
知らないかたも少しはいらっしゃるかと思うので簡単にflutterの説明をすると、flutterはアプリを作るためのフレームワークというもので、Dartという言語を使ってやるものです。多分。笑
フレームワークっていうのは自分もよくわかっていないんですけど、頭いい人たちが簡単にアプリ開発ができるよう手助けしてくれるツールかサンプルコードとかを作ってくれたものみたいな解釈をしています。コードはみんなが載せてるだけでフレームワークっていうくくりじゃないのかな?ちょっとよくわかってないですけどまあとりあえずこれ使うと、なにもないよりは簡単に作れるよってものらしいです。
そしてこれはなかなか最近できたものらしくて、わからないことについてgoogle検索をかけても、英語の説明しか出てこないし、日に日にアップデートされていて、コードの書き方がどんどん変わっていってるみたいなので、ほんとに勉強するのが大変なんです。笑
なので成長しているスピードがとてつもなく遅いので、こうやって記事をかくこともあまりないのかなと思って投稿せずにやってました。
でもよく考えたらトラブルだらけの方が書くこといっぱいあるのかな。笑
今度わからないことだらけのflutter勉強の様子も書いてみようと思います。
ところで、急になんでCの勉強に帰ってきたのかと申しますと、flutterのほうでトラブルが発生して、実行ができなくなっているので、今度父と会う時まで、flutterの勉強ができない状態になりました。笑
なので今日は日本語で学べるC言語を爆速で進めていきたいと思っています!
ほんとに父に聞けるからいいものの、なにもわからない状態から一人で始めて成り上がっていくのは大変だなと感じます。
ほんとに家族に恵まれているのでこれを無駄にしないように頑張っていきたいと思います。
ではいきましょう!
-このシリーズはこの本を見ながら進めています-
・関数
ついに自作の関数を作り始めます。
ここら辺から複雑になってきそう。
今まで自分たちが使ってきた関数も、誰かがもっと大変な機械語とかからつくってきたと考えると、ほんとに誰かの努力の積み重ねで今のこの言語が出来上がっていることに感謝しないとな。違かったら恥ずかし笑
ここもやったことがあるので、早速演習6-11をやっていきます。
# include <stdio.h>
int search_idx(const int v[], int idx[], int key, int n){
int count = 0;
for (int i = 0; i < n; i++){
if(v[i] == key){
idx[count] = i;
count++;
}
}
return count;
}
int main(void){
int v[] = {1,2,4,6,1,6,8,1,4,6,7,24,};
int check = 1;
int vele = 12;
int idx[vele];
int howMany = search_idx(v, idx, check, vele);
for(int i = 0; i < howMany; i++){
printf("%d\n", idx[i] + 1);
}
}
実行結果
1
5
8
久しぶりにC言語書いたけど色々合ってるか不安だ。笑
最近授業ではPythonを扱っているので、;をめちゃくちゃ忘れました。
int search_idx(const int v[], int idx[], int key, int n){
int count = 0;
for (int i = 0; i < n; i++){
if(v[i] == key){
idx[count] = i;
count++;
}
}
return count;
}
僕が今作った関数はこちら。
関数というのは値を受け取って値を返すのが基本になります。例えば2と3を受け取って足して5を返すのような感じで。
その返す型を最初に書きます。今回は整数を返すので、int。
次に来ているsearch_idxというのが関数の名前。でその後のかっこにさっきの例でいう2と3みたいな受け取るものが書いてあります。この受け取るもののことを仮引数と呼ぶみたい。ここでは
int型の配列のvとidx、
int型のkeyとnの4つですね。
その次の括弧の中に処理が書かれていて、returnによってさっきの例でいう5のような返す値を定めます。
int howMany = search_idx(v, idx, check, vele);
そしてこれが作った関数を呼び出しているところ。
関数の名前を書いた後に、引数を関数を定義したときにきめた仮引数の数だけ同じ順番に同じ型になるように書いていきます。これを実引数というみたい。
上の仮引数は関数の中で使う名前で、受け取った実引数に関数定義の文の中だけで通用する名前を新しくつけているというイメージ。
関数の説明はこれくらいにして、この演習の説明に入ります。
この演習は、
v → 整数のリスト
idx → 空のリスト
key → 整数
n → vに入っている整数の個数
が入っていて、
b → vの中にあったkeyの数
idx → 何個目に入っていたかの引数の配列
を返すというものです。
ここで一つ問題が浮上。idxにはindexが入るんだけど、何個入るかわからん。そしてmallocとかやってないので使っちゃダメ。mallocもうどんなのか忘れたけど笑
まあよくわからんけどvの要素の数を超えることはないので、vと同じ要素数の配列を定義することにしました。
他は何か迷うこととかはなく、まだ余裕をもっていけています。
どんどん次行きましょう。
・有効範囲
次は有効範囲についてです。
有効範囲というのは、ここでは定義した変数が使える範囲のことを示していて、基本的に{}をまたぐとその変数は使えなくなるみたい。
{}の中で外と同じ名前の変数を定義すると、その中では新しく定義した時に代入したものとして扱われ、その{}を抜けるとその前に定義した時に代入したものに戻るらしい。
二つも同じ名前の変数を定義するのはわかりにくいのでしないと思うけど、なにか想定と違う挙動をした時に意図せず同じ変数名で定義しているかもしれないというアンテナをはるのは大切かもしれないな。
ということで挙動確認めんどいので次行っちゃいます笑
そして次は型についての詳しい解説とか計算の優先順位とかがあるけど、書くととんでもない量になるので、読み終わったので次に進みます。
・関数形式マクロ
次は関数形式マクロというものです。
関数形式マクロは、関数を作るほどじゃない時に、名前の右に式とかを書くとmain関数の中とかにその名前があると、その式にそのまま置き換えてくれるシステムです。
文章だとわかりにくいので、実際に書いてみます。
#include <stdio.h>
#define double(x) ((x) + (x))
int main(void){
int n = 2;
printf("%d\n",double(n));
}
この#defineの部分が関数形式マクロです。
double(x)という形の文が出てくると、その部分を((x) + (x))に置き換えます。
だからこれは4と表示されます。
計算してくれるのではなく、ただそこの部分が置き換わるだけなので、nの中身がintじゃなくてもこの文章は成り立つのがいいところみたいです。%dのところは変えないといけないのかもしれないけど。
確かに繰り返し出てくる何かをこんな簡単に置き換えられるなら結構良さそう。
ちなみにほんとに置き換わるだけらしいので、
#include <stdio.h>
#define double(x) ((x) + (x))
int main(void){
int n = 2;
printf("%d\n",double(n++));
}
とやると、xには2+1が代入され、さらに((n++) + (n++))と展開されるので、4 + 4になるのかな?よくわからないけどどうやらこうやるとまずいらしいです。
実行してみたら警告が出ました。優秀や。
無理矢理実行したら5になったので、まあよくわからんと。笑
まあこういうふうには書かないようにします。
#include <stdio.h>
#define double(x) (x * x)
int main(void){
int n = 2;
printf("%d\n",double(3 + 5));
}
例をもう一つやってみます。
これをやると、64が出てくると思いきや、23がでてきます。
なんでかっていうと、3 + 5 * 3 + 5となって、掛け算が先に計算されるからです。
だから、いちいち定義に出てくる要素は()で括った方がいいみたいです。
ということで演習やっていきます。
8-1
xとyの差を求めるものをつくる
#define diff(x, y) (((x) > (y)) ? ((x) - (y)) : ((y) - (x)))
カッコ多いわ笑
これは簡単なので実行しなくてもいいと思ったけど不安なのでやってきました。
いけてましたセーフ。
三項演算子初めて使ったけど便利だなこれ。
はい次。
8-2
#define max(x, y) (((x) > (y)) ? (x) : (y))の時
1、max(max(a, b), max(c, d))
2、max(max(max(a, b), c), d)の違いについて考える
2を展開すると、
(((((((a) > (b)) ? (a) : (b)) > (c)) ? (((a) > (b)) ? (a) : (b)) : (c)) > (d)) ? ((((((a) > (b)) ? (a) : (b)) > (c)) ? (((a) > (b)) ? (a) : (b)) : (c)) : (d))
うーん。ちゃんとカッコで囲まれてるから変な挙動しなさそうだけどな。
まあよくわからんし実行してみてもちゃんと同じく最大が表示されるから飛ばして次いきまーす笑
8-3
swap(type, a, b)
でa, bにはtypeに書いてある型の何かが代入され、aをbに、bをaに代入すると。
無理。どんなに考えてもできない。数字限定ですらできないけど複雑な計算式なんかやったら文字来たらどうすんだよ。
ということで調べてみたら、なんと正解を乗っけてくれているサイトを発見!
いやーまじでありがたい。
答えは
do{type t = a ; a = b ; b = t;}while(0)
らしい。なんだそれ。do文かけんのかよ。しかもtype tで変数定義できるのかよ。
書いてないこと使わないでほしいけど2年くらい考えたらできんのかな?笑
それとも書いてあったんかな?笑
まあ知らないことは知らないので落ち込まず次行きましょう。
・ソート
ソートとは、データをある規則によって並べ替えることだと僕は解釈しています。
演習やっていきます。
8-4
配列とその要素数を与えられて、その配列を小さい順に並べ替える関数を作るというものです。
#include <stdio.h>
#define NUMBER 5
void bsort(int a[], int n){
for (int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
if(a[j] > a[j + 1]){
int change = a[j];
a[j] = a[j + 1];
a[j + 1] = change;
}
}
}
}
int main(void){
int i;
int height[NUMBER] = {123, 176, 173, 276, 143};
bsort(height, NUMBER);
for(int i = 0; i < NUMBER; i++){
printf("%2d番:%d\n", i + 1, height[i]);
}
}
実行結果
1番:0
2番:123
3番:143
4番:173
5番:176
これは簡単でした。スラスラいけた。
少しは成長したんじゃない?嬉しいですね。
次行きましょう!
・列挙体 enum
次は列挙体です。
よくわからんけど説明を読んで自分なりに説明します。
enum subject { Math, Japanese, English, Science, SocialStudies, invalid}
というふうに宣言すると、(スペルミスってたらごめんなさい)
enum subjectという型が作られて、前から順に0から番号が振り分けられます。
だけらしい。なんだこれ。別に引数一つ一つに数字割り当てれば良くね。でも他の人が読んだ時にこれ使われていると並列の選択肢であることがわかりやすいとか何かしらメリットがあるのでしょう。
ちなみに
enum subject { Math, Science =0, English = 1, Japanese, SocialStudies, = 2 invalid}
みたいに複数の選択肢に同様の数字を割り振ることもできるみたいです。
てかこれ入り切らないとスクロール式になるんや。
そして代入がされてないものにはその前の値に1を足したものが入るみたい。
だからinvalidには3が代入されます。
まあこのままだとよくわからないので、演習8-5がちょうど自由にこれを使ってみようという課題なので、書いてある例文を参考に自分なりに書いてみます。
#include <stdio.h>
enum subject { Math, Science, English, Japanese, SocialStudies, Invalid};
void result1(void){
puts("さてはあなたは理系ですね?");
}
void result2(void){
puts("さてはあなたは帰国子女ですね?");
}
void result3(void){
puts("さてはあなたは文系ですね?");
}
enum subject select(void){
int tmp;
do {
puts("好きな教科は?");
puts("0•••数学 1•••理科 2•••英語 3•••国語 4•••社会 5•••終了");
scanf("%d", &tmp);
} while (tmp < Math || tmp > Invalid);
return tmp;
}
int main(void){
enum subject selected;
do {
selected = select();
switch (selected){
case Math : result1(); break;
case Science : result1(); break;
case English : result2(); break;
case Japanese : result3(); break;
case SocialStudies : result3(); break;
}
} while (selected != Invalid);
}
実行結果
好きな教科は?
0•••数学 1•••理科 2•••英語 3•••国語 4•••社会
0
さてはあなたは理系ですね?
好きな教科は?
0•••数学 1•••理科 2•••英語 3•••国語 4•••社会
1
さてはあなたは理系ですね?
好きな教科は?
0•••数学 1•••理科 2•••英語 3•••国語 4•••社会
2
さてはあなたは帰国子女ですね?
好きな教科は?
0•••数学 1•••理科 2•••英語 3•••国語 4•••社会
3
さてはあなたは文系ですね?
好きな教科は?
0•••数学 1•••理科 2•••英語 3•••国語 4•••社会
4
さてはあなたは文系ですね?
好きな教科は?
0•••数学 1•••理科 2•••英語 3•••国語 4•••社会
5
こんな感じでした。
いや使ってて思ったけどやっぱこれいる?普通に入力してもらって、こんな変な変数使わなくてもcaseのところに数字をいれれば普通に完結するとしか思えないんだが。
しかも警告出てたし。色々変えてみたけど結局何が原因かは良くわかりませんでした。
まあ想定通りの挙動をしたのでよしとしましょう。
enumの存在意義が全くわからなかったし、複数の選択肢を同じ数字に格納して場合分けを少なくしようとしたけど、結局入力の時に同じ数字を入力してもらうことになって、じゃあ変わらんやんってなったし。
やっぱりまだまだ未熟みたいです笑
はい!次!
・再帰関数
はいでました。ついに再帰です。
これほんとに最初難しかった。初めての方はここで詰まるかもしれません。
でも僕は少し前とは違います。再帰を使ったプログラムを少し書いたのでもう怖いものはありません。きっとね!
再帰の簡単な説明は、関数の中に同じ関数を入れるという複雑な構造になっています。
下に例を示します。
#include <stdio.h>
int factorical(int n){
if(n > 0){
return n * (factorical(n - 1));
}
else return 1;
}
int main (void){
int num = 6;
printf("%d\n", factorical(num));
}
実行結果
720
factoricalを6で呼び出すと、6 * factorical(5)が返ってきます。
そしてfactorical(5)は5 * factorical(4)を返してくるので、6 * 5 * factorical(4)となります。
これを繰り返すと6から1までかけた値が返ってきて、720となります。
これが再帰関数の基本ですね。
この例えなら結構わかりやすいと思いますが、僕が最初知った時は最初から難しいプログラムを見せられて、かなり理解に苦しんだのを覚えています。
とはいえ別に今が経験豊富なわけでは全くないので、今もわからないものも多いですけどね。笑
では練習問題行ってみましょう。
8-8で、最大公約数を求める問題です。
#include <stdio.h>
int gcd(int x, int y){
if(x == y) return x;
else if(x > y) return gcd(x - y, y);
else return gcd(y - x, x);
}
int main (void){
printf("%d\n", gcd(1767, 2541));
}
実行結果
3
2数の最大公約数というのは、二つのうち小さい方と2数の差の最大公約数と等しくなります。
知らなかった方はユークリッドの互助法で検索してみてください。
ということで、1767と2541の最大公約数を求めるように呼び出すと、3が出てきました。
両方3でわれるので、多分あってるでしょう!
まあ再帰は散々苦しめられたのでもう大丈夫だと信じたいです。
今回はこの辺で。
・まとめ
いかがだったでしょうか。
このシリーズは自分の試行錯誤の段階を皆さんに見届けていただくというコンセプトなのに、結構授業で無理矢理色々やらされたが故にいままであまり苦労していないので、読者の皆さんにとって美味しい展開が少なくてちょっと困っています。笑
この本は早く終わらせて、すぐ次に行って早く皆さんに僕が苦しんでるところを見てもらって、プログラミング教えてる方の参考になるように、もうできる方にニヤニヤしながら見てもらえるように、一緒に進んでる方を元気付けられるように、そして同じとこで詰まった方の参考になるように、がんばりたいと思いますので、応援してくださる方は、スキとフォローよろしくお願いします!
励みになるので是非!
その他要望や質問、僕の疑問に答えてくださる方がいらっしゃれば是非コメントしていただけると幸いです。
ではまた!