# みんなで頑張るプロ言 続編 / 模擬問題 * 前編テキスト編:https://hackmd.io/@sakarush/rkNdiuHxI * プログラミング言語のテスト予想問題です。 * 先生の出してる情報をもとに予想していきます、当てたい... * 解答は後ろにのせました。 * コードを試してみたかったら、オンラインコンパイラが便利です。使ってみては? https://paiza.io/projects/1-GQS58EW33wzS_-4ZmDfQ?locale=ja-jp ## 予想問題1(練習10の発展) * 1から100までの値nと101から200までの値mを入力させる。入力を促す適切な文を表示すること。入力させるインプット文は英語で表示すること。 * 一度にnとmを同時に入力させること。 * 読み込んだn,mが条件を満たしていなかったら入力させ直す。 * n以上m未満で、5で割りきれる数を小さい順に表示する。 * n以上m未満で、7で割りきれる数を大きい順に表示する。 [答え](#模範解答1) ## 予想問題2.1 - 易(練習12の発展) * 1から12までの整数を入力させる。入力を促す適切な文を表示すること。また英語で表示すること。 * 入力が条件を満たしていなかったら入力させなおすこと。 * 入力されたら「○○月は□□日あります」という文を英語で表示すること。 * 〇〇月に関する条件分岐には、switchを使うこと。 * うるう年では無いとします。 [答え](#模範解答2.1) ## 予想問題2.2 - やや難(練習12の発展) * 以下を繰り返す * 0から12までの整数を入力させる。入力を促す適切な文を表示すること。また英語で表示すること。 * 入力が条件を満たしていなかったら入力させなおすこと。 * 入力された数字が0だったら「終了」と英語で表示して終了すること。 * 入力されたら「○○月は□□日あります」という文を英語で表示すること。 * 最初から、もう一度入力させる。 * うるう年では無いとします。 [答え](#模範解答2.2) ## 予想問題3(練習15の類題) * 3つの自然数を入力させる。入力を促す適切な文を表示すること。また英語で表示すること。 * 一度に3つの1以上の自然数を入力させること。 * もしどれか一つでも自然数でなかったら、全て入力させなおすこと。 * 入力された3つの数字は、三角形の辺の長さを表しているとし、まず三角形としてありえない場合には「これは三角形ではありません」と英語で表示して終了すること。 * 入力された三角形が「鋭角三角形」「鈍角三角形」「直角三角形」のどれに属するかを判定して英語で表示する。 なお、それぞれ英語は以下の通りらしいです(ぐぐった) * 鋭角三角形 : acute triangle * 鈍角三角形 : obtuse triangle * 直角三角形 : right triangle とか right-angled-triangle <details><summary>ヒント(クリックしたら見えます)</summary><div> ``` 鋭角三角形、鈍角三角形、直角三角形の条件は以下の通りでしたね 長辺をc、それ以外をa,bとすると 直角 : c^2 = a^2 + b^2 鋭角 : c^2 < a^2 + b^2 鈍角 : c^2 > a^2 + b^2 ``` </div></details> <br> [答え](#模範解答3) # 解答編 * プログラム中に書いてある``//`` はコメントと言って、その行のそれ以降がプログラムとして認識されなくなるので、メモ書きが出来ます。 例: ```C int a = 0; // ここはコメントなので何書いてもいいwwwwww ``` * **コメントは皆さんへの説明のために足しているものなので、テストでは書いてはいけません**(ちゃんと//とか書き方間違えなければ書いてもいいとは思うんですが、そもそも必要ない上に写したことがバレバレになります) * プログラムは答えが一通りでは無いので、違うからって間違ってるとは限りません。 模範解答のコードは一応動作確認はしたつもりですが、間違えてるかも! * 変だなと思ったり、間違えてたり、分からなかったりしたらいつでもLINEで聞いて下さい。 * Windowsの環境とは異なり、改行コード「¥n」が「\n」として表示されていますが、どちらでも一緒です。テストでは「¥n」って書いたほうがいいのかもしれません。(本質的にはどちらも一緒の改行コード) ## 模範解答1 ```C #include <stdio.h> int main(void){ int n, m; do { printf("Please input 2 numbers ( 1 <= n <= 100 , 101 <= m <= 200) : "); scanf("%d %d", &n, &m); //scanfでは変数名の前に&を忘れない! }while(n <= 0 || n >= 101 || m <= 100 || m >= 201); //まずは5で割り切れる数を表示 for (int i = n; i < m; i++){ // nからm未満まで (もしm以下なら i <= m にする!) if (i % 5 == 0) { // i を 5 で割ったあまりが0ならば、つまり割り切れたら printf("%d is divisible by 5\n",i); // 5で割り切れた数を表示 } } //次に7で割り切れる数を"大きい順に"表示 for (int i = m - 1; i >= n; i--) { // iをm - 1からはじめて、n以上の間繰り返す。どんどん減らしていくので i-- を使う! if (i % 7 == 0) {// i が 7 で割り切れたら printf("%d is divisible by 7\n",i); } } } ``` ### 解説 この問題は基本的には例題と一緒なんですが、肝は「7で割り切れる数を大きい順に表示する」です。 今まではiをいつも小さい数から大きい数まで動かしていたので、これを逆にします。 つまり m - 1からi >= n までを繰り返すのです。 そして、iは下げていく必要があるので、i++ではなくi--とします。 (もしmからスタートすると、mを含んでしまうので未満ではなくなる。) このあたりのforの正確な書き方は重要なので覚えておきましょう。 また問題が以下なのか未満なのかをよく見て<か<=とかの等号を調整しましょう。 また、do whileの書き方は定型文なのでマスターしておきましょう。 ```C int 変数 do{ printf("インプット文"); scanf("%d",&変数); }while(もう一度やり直す条件); ``` ## 模範解答2.1 英語は苦手です... ```C #include <stdio.h> int main(void){ int x; do { printf("Please input month : "); scanf("%d", &x); //scanfでは変数名の前に&を忘れない! }while(x < 1 || x > 12); switch(x) { case 2: printf("%d month has 29 days.\n",x); break; case 4: case 6: case 9: case 11: printf("%d month has 30 days.\n",x); break; default: // 2,4,6,9,11以外は全て31日なので、デフォルトに入れてもよい(1 <= x <= 12が保証されている限り) printf("%d month has 31 days.\n",x); } } ``` ### 解説 これは最も基本的なswitch文の使い方です。 今回は扱う変数名をxとしましたが、都度都度最初に自分で決めた変数名にしてくださいね(最初にint x;しておきながら、後でいきなりa使うとかはNG) ちなみにdo while(条件文)構文の、条件文には今``x < 1 || x > 12``を入れています。これは「xが1未満 または xが12よりも大きい」ならばやり直す という意味です。 ## 模範解答2.2 ```C #include <stdio.h> int main(void){ int x; while(1) { // これは無限ループ do { printf("Please input month or Zero(Finish): "); scanf("%d", &x); //scanfでは変数名の前に&を忘れない! } while (x < 0 || x > 12); if (x == 0) { // xが0なら終了 printf("Okay! Finish!"); return 0; } switch (x) { case 2: printf("%d month has 29 days.\n", x); break; case 4: case 6: case 9: case 11: printf("%d month has 30 days.\n", x); break; default: // 2,4,6,9,11以外は全て31日なので、デフォルトに入れてもよい(1 <= x <= 12が保証されている限り) printf("%d month has 31 days.\n", x); } } } ``` ### 解説 先程の問題2.1を、何度も入力できるようにしたプログラムです。 ここでの肝は ``` while(1) { } ``` です。 思い出してみましょう。while(条件式) は()の中の条件式が正しい間はリピートする繰り返し文でしたね。 そして、C言語では1は正しいとされていますので(TRUEと等しい)while(1)は常に実行されます。 言い換えれば**無限に実行される無限ループ**です。たまにプログラム初心者が意図せずこの無限ループを作り出してしまう罠にハマります。 ちなみに、無限ループを作るときに、while(条件文)の条件文は正しければ何でもいいので、例えばwhile(1 == 1)とかwhile(0 == 0)とかでも同じことです。while(1)と書く人のほうが多いとは思いますが、while(1 == 1)のほうが直感的かもしれません。どっちでも大丈夫です。ただ**while(0)は一度も実行されないので、ダメです**よ 今回は、0と入力されたときに終了すればいいので、とりあえず無限ループで囲った後に、入力された値x が 0 のときにreturn 0;で終了しました。 return 0; は(少なくとも習っている範囲においては)プログラムを終了させる命令だと思えばよいです。 ## 模範解答3 ```C #include <stdio.h> int main(void){ int a, b, c; do{ printf("Please input lengths of triangle's sides : "); scanf("%d %d %d", &a, &b, &c); }while( a <= 0 || b <= 0 || c <= 0); //まずは並び替える。aが一番大きくなるようにする。 int tmp; if (a < b) { // a と b で、bのほうが大きかったら入れ替える tmp = b; b = a; a = tmp; } if (b < c){ // b と c で、cのほうが大きかったらいやなので入れ替える tmp = c; c = b; b = tmp; } if (a < b) {//最後にもう一度aとbで入れ替えれば、cに最大があってもaまで運ばれる tmp = b; b = a; a = tmp; } // a * a の部分は a^2 と書いてはいけない!(後述) if (a >= b + c) { // まず三角形でなかった場合 printf("It is not Triangle!!\n"); }else if (a * a == b * b + c * c){ // 直角三角形ならば printf("It is right Triangle!!\n"); }else if (a * a < b * b + c * c){ // 鋭角三角形なら printf("It is acute Triangle!!\n"); }else{ printf("It is obtuse Triangle!!\n");// どれでもなければ鈍角三角形 } } ``` ### 解説 まず後半のifの部分はそんなに問題無いのでは無いでしょうか? if は「もしも ~ ならば」 else は「そうでなかったら」と言い換えて読んでみましょう 上から順に もし a >= b + c ならば 三角形でない そうでなくて、 a * a == b * b + c * c ならば 直角三角形 そうでなくて、 a * a < b * b + c * cならば 鈍角三角形 そうでなかったら、鋭角三角形 このように読むと理解しやすいです。 三角形で、直角でも鋭角でもなかったら自明に鈍角三角形ですよね 次に、並べ替えをしている部分について解説します。 これはバブルソートと呼ばれる並べ替えの方法で、前後で比較して入れ替えていきます。 目指すのは a >= b >= c の形です。 もし aとbを見比べてbのほうが大きかったらこれは良くないので、aと入れ替えます。(入れ替えるコードの部分については後で解説します。) 次に b とcを比べて、同じことをします。 これで終わりではありません!もしcに最大の数値があったときに、aまで運ばれずに終わってしまいます。 例えば、最初が 1 3 5 だったとき、 3 1 5 にしてから 3 5 1 になりました。 本来ならば 5 3 1がいいのに、5が真ん中に残っています。 ということで最後にもう一度aとbを比較します。 aとbを比較、必要なら入れ替える bとcを比較、必要なら入れ替える aとbを再比較、必要なら入れ替える という手段を踏んではじめてaが最大になりました。 この並べ替え(英語でsort、ソートと言います)のやり方は定番ですし、少し難しいので理解出来なくても覚える / 書いておくくらいはしておきましょう。 次に入れ替えの処理について解説します。 要はやりたいことは a の中身をbにうつして、bの中身をaにうつすという動作です。 気持ち的には次のように出来たら楽ですね ``` a = b; b = a; ``` しかしこれでは問題があります。プログラムは上から1行ずつ実行されるので、全ての数がbになるのです。 試してみましょう。aとbをそれぞれ 3 6とします。 a = b; この時点でaが6になり、bは6のままです。 b = a; aは既にここでは6なので、aもbも6になってしまいました。 入れ替えるはずが、aの値を消してしまっただけです。 そこで、aの値を一時的に外に保管しておきます。そのための変数がtmp(Temporary、一時的な変数みたいな意味でこの名前をつけました)です。 tmp = a; a = b; b = tmp; これならば、aの値をtmpに避難させておき、aにbの値を移します。そして避難させておいたaの値をbに代入し直して、入れ替え完了です。 ちなみに **絶対にa^2と書いてはいけません**。a*aというふうにしてください。 ``` 発展事項 なんでa^2がダメか 覚えていなくても(書かなくて)大丈夫です。 結論から言うと、^の記号がn乗っていう意味を持っていないからです C言語において^は別の意味を持った演算子で、全く結果が変わります。 ちなみにC言語における^はビット演算子といって、2進数のビットを操る演算子になります。 具体的にはXORを取る演算子です。 このあたりは知らなくても一切問題ありませんが、 2乗を表したい時はx ^ 2ではなく x * xと書くようにしましょう。 余談ですが、C言語で2乗をつくりたい時には pow(x,2); という関数を使うことでも実現出来ます。 しかしこれは習ってないので使えません。(それに#include "cmath.h"というのを最初に書くことになります。) また、他の言語では x ** 2 と書くと実現出来るプログラミング言語もあります。 C言語では使えないのでどのみちダメですが ``` # 終わりに 結構適当に解説した & 特に最後の問題適当にテストしただけなのでどこか間違ってるかもしれません。 どれかの問題だけでも当たれば嬉しいですね 間違えを見つけたり、わかりにくい表現があればどんどん教えて下さい~ じゃあ最後のテストがんばっていきましょ~