電球がONである数を数えるプログラム
問題
1からnまでの数字が振られたn個の電球がある。これらの電球に対して、次の操作を順に行う。
・1の倍数の電球のON/OFFを切り替える。
・2の倍数の電球のON/OFFを切り替える。
・mの倍数の電球のON/OFFを切り替える。
このとき、最終的にONになっている電球の数を出力するプログラム作成せよ。最初は全ての電球がOFFになっているものとする。
入力例 出力例
12 3 電球ONの数:6
25 9 電球ONの数:14
500 162 電球ONの数:256
1000 1000 電球ONの数:31
コード
#include <stdio.h> #include <stdlib.h> //電球をnの倍数で切り替える処理 void switchBulbs(int *bulbs, int multiple, int count){ for(int num = multiple-1; num < count; num += multiple){ if(bulbs[num] == 0){ bulbs[num] = 1; }else{ bulbs[num] = 0; } } } int main(void){ int n,m; scanf("%d%d",&n,&m); int *bulbs = (int*)malloc(n * sizeof(int)); for(int num = 0; num < n; ++num){ bulbs[num] = 0; } for(int num = 1; num < m + 1 ; ++num){ switchBulbs(bulbs, num, n); } int sum = 0; for(int num = 0; num < n; ++num){ if(bulbs[num] == 1){ ++sum; } } free(bulbs); printf("電球ONの数:%d",sum); }
入力
1000 1000
出力
電球ONの数:31
ただ、もっとスマートなコードがあったので
あまり宛てにしないでください。。。orz
2次元配列を使ったプログラム
#include <stdio.h> int main(void){ //NOTE:本当は、inputを可変長の配列にすると使い勝手がいい int input[2][5] = {}; for(int row = 0; row < 5; ++row){ scanf("%d%d",&input[0][row],&input[1][row]); } int rowSize = sizeof(input[0]) / sizeof(input[0][0]); int colomnSize = sizeof(input) / sizeof(input[0]); printf("======各行の平均======\n"); for(int row = 0; row < rowSize; ++row){ printf("%d %d %0.1f\n",input[0][row],input[1][row],(float)(input[0][row]+input[1][row])/colomnSize); } printf("======各列の平均======\n"); int sum[2] = {}; for(int row = 0; row < rowSize; ++row){ for(int colomn = 0; colomn < colomnSize; ++colomn){ sum[colomn] += input[colomn][row]; } printf("%d %d\n",input[0][row],input[1][row]); } printf("%0.1f %0.1f\n",(float)sum[0]/rowSize,(float)sum[1]/rowSize); }
入力
70 65 92 83 75 85 66 74 89 92
出力
======各行の平均====== 70 65 67.5 92 83 87.5 75 85 80.0 66 74 70.0 89 92 90.5 ======各列の平均====== 70 65 92 83 75 85 66 74 89 92 78.4 79.8
身長と標準体重の対応表を表示するプログラム
printfとscanfもっとうまく書ける気がする。。
#include <stdio.h> int main(void){ int from,to,each; printf("何cmから:"); scanf("%d",&from); printf("%d\n",from); printf("何cmまで:"); scanf("%d",&to); printf("%d\n",to); printf("何cmごと:"); scanf("%d",&each); printf("%d\n",each); for(from; from <= to; from = from + each){ float stdWeight = (from - 100) * 0.9; printf("%dcm %0.2fkg\n",from,stdWeight); } }
入力
150 190 5
出力
何cmから:150 何cmまで:190 何cmごと:5 150cm 45.00kg 155cm 49.50kg 160cm 54.00kg 165cm 58.50kg 170cm 63.00kg 175cm 67.50kg 180cm 72.00kg 185cm 76.50kg 190cm 81.00kg
半角文字列を入力し、英字のみの文字列と数字のみの文字列に分解して出力するプログラム
最近Cばっか記事に書いてるなぁ
C言語はどうしても他の言語と比べてコード量が多く読みづらくなっちゃうから
あんまり好きじゃないんだ
80文字指定って言われたので配列にしたけど、
サイズ無制限にしたいってなったときは
文字列をcharのポインタを使いたいよね。
あほだから書き方が微妙に分からんです。。
#include <stdio.h> #include <string.h> int isNumeric(char moji){ if( (moji>='a'&& moji<='z') || (moji>='A' && moji<='Z')){ return 0; } else if(moji>='0'&& moji<='9'){ return 1; } return -1; } int main(void){ char input[80] = ""; printf("半角文字列を入力:"); scanf("%s",input); printf("%s",input); char numeric[80] = ""; char string[80] = ""; int numericLen = 0; int stringLen = 0; int len = strlen( input ); for(int pos = 0; pos < len; ++pos ){ if(isNumeric(input[pos])){ numeric[numericLen++] = input[pos]; } else{ string[stringLen++] = input[pos]; } } if(strlen(string)){ printf("\n英字のみの文字列「%s」",string); } else{ printf("\n英字は1文字も入力されていませんでした。"); } if(strlen(numeric)){ printf("\n数字のみの文字列「%s」",numeric); } else{ printf("\n数字は1文字も入力されていませんでした。"); } return 0; }
【パワー系PG】変数を連番で命名はNG!配列やコレクションを使おう
あなたのプログラム、「変数を連番で命名」してませんか?
まさかいないとは信じたいけれど。。
けれども、
某質問サイトのユーザーから頂いたコードを拝見した結果…
存在した。存在してしまった!
(しかも意外に多い…)
先輩や知人、友人等に教わる人がいなかったのだろう。
少し可哀想だと思い、記事を作った。
変数を連番で命名しちゃっている方は、
今からでも遅くない!
しっかり覚えて、修正していこう。
今回は「五十音を取り扱うプログラム」を題材にした。
以下を出力するプログラム
あいうえお かきくけこ さしすせそ たちつてと なにぬねの はひふへほ まみむめも やゆよ らりるれろ わおん
コレクション(List等)を使いたいので書きやすいC#で書きます。
まず駄目コードから。
パワー系PGのコード
using System; public class GojuonProgram{ public static void Main() { string value1; string value2; string value3; string value4; string value5; string value6; string value7; string value8; string value9; string value10; value1 = "あいうえお"; value2 = "かきくけこ"; value3 = "さしすせそ"; value4 = "たちつてと"; value5 = "なにぬねの"; value6 = "はひふへほ"; value7 = "まみむめも"; value8 = "やゆよ"; value9 = "らりるれろ"; value10 = "わおん"; Console.WriteLine(value1); Console.WriteLine(value2); Console.WriteLine(value3); Console.WriteLine(value4); Console.WriteLine(value5); Console.WriteLine(value6); Console.WriteLine(value7); Console.WriteLine(value8); Console.WriteLine(value9); Console.WriteLine(value10); } }
パワーを感じるぞ。。
書くの疲れた。。
突っ込みたいのは何点か
- valueが何なのか変数名だけ見てるとわからん
- や行を「やいゆえよ」にしたいとき変数を一々上から見ていかないといけない
とか、色々ある。
はい、じゃあ配列を使おう。
配列を使った実装
using System; public class GojuonProgram{ public static void Main() { string[] gojuon = new string[] { "あいうえお", "かきくけこ", "さしすせそ", "たちつてと", "なにぬねの", "はひふへほ", "まみむめも", "やゆよ", "らりるれろ", "わをん" }; for(int line = 0; line < gojuon.Length; ++line) { Console.WriteLine(gojuon[line]); } } }
大分楽になったね。
これならやゆよはgojuonっていう配列1個を修正すればいいってすぐに分かる。
個人的には配列の要素0が「あ行」、要素1が「か行」って言う
作りがあんまり直感的じゃなくて嫌だなぁと思ってる。
コレクション(List)を使った実装
using System; using System.Collections.Generic; public class GojuonProgram{ public static void Main() { List<string> gojuon = new List<string> { "あいうえお", "かきくけこ", "さしすせそ", "たちつてと", "なにぬねの", "はひふへほ", "まみむめも", "やゆよ", "らりるれろ", "わをん" }; foreach(var line in gojuon) { Console.WriteLine(line); } } }
配列と似たような作りに見えるね。
ただ、配列とリストは似ているようで、ぜんぜん違う。
データの構造が違う。
↓が参考になるかな。
qiita.com
大雑把に言うと
配列は要素数が固定。
リストは要素数が可変。
今回のプログラムでは
gojuonというデータに対して
"きゃきゅきょ"や"しゃしゅしょ"、"ちゃちゅちょ"なども
追加する想定であれば、リストを使うべき。
そうでなければ、
ランダムアクセスが早い(=処理速度に影響する)ので、
配列を使うべき。
↑に書いたとおり、
配列の要素数は「固定」なので、
宣言時にしか要素数を指定できない。
配列を大きさの違う配列に移すことは可能だが、
頻繁に移すようならコレクションを使おう。
あと、C#のクラスだが、自分ならDictionaryを使いたい。
コレクション(Dictionary)を使った実装(とLINQを少々。。)
using System; using System.Collections.Generic; using System.Linq; public class GojuonProgram{ public static void Main(){ Dictionary<string, string> gojuon = new Dictionary<string, string>(); gojuon.Add("あ行","あいうえお"); gojuon.Add("か行","かきくけこ"); gojuon.Add("さ行","さしすせそ"); gojuon.Add("た行","たちつてと"); gojuon.Add("な行","なにぬねの"); gojuon.Add("は行","はひふへほ"); gojuon.Add("ま行","まみむめも"); gojuon.Add("や行","やゆよ"); gojuon.Add("ら行","らりるれろ"); gojuon.Add("わ行","わをん"); Console.WriteLine("******キーと値を交互出力******"); foreach(var line in gojuon) { Console.WriteLine(line.Key); Console.WriteLine(line.Value); } Console.WriteLine("******キーを元に値を表示******"); Console.WriteLine(gojuon["さ行"]); //さ行の文字列がほしい! Console.WriteLine(gojuon["わ行"]); Console.WriteLine("******五十音の中で文字列のサイズが5の行を抽出し出力******"); foreach(var line in gojuon.Values.Where(v => v.Length == 5)) { Console.WriteLine(line); } } }
出力
******キーと値を交互出力****** あ行 あいうえお か行 かきくけこ さ行 さしすせそ た行 たちつてと な行 なにぬねの は行 はひふへほ ま行 まみむめも や行 やゆよ ら行 らりるれろ わ行 わをん ******キーを元に値を表示****** さしすせそ わをん ******五十音の中で文字列のサイズが5の行を抽出し出力****** あいうえお かきくけこ さしすせそ たちつてと なにぬねの はひふへほ まみむめも らりるれろ
便利な機能は逐次利用していこう。
【パワー系PG】データを整数型で表現しないで列挙型を使え
あなたのプログラム、「intを不必要に多用」してませんか?
どの言語にもenumみたいな列挙型ってあると思うんだ。
C言語における、intとenumによる実装の違いを紹介しよう。
intを使ったときの実装
#include <stdio.h> int main(void){ int warningRate; //2桁入力【例:36】 scanf("%02d", &warningRate); int signal; if(warningRate < 50){ signal= 0; } else if(warningRate < 75){ signal= 1; } else{ signal= 2; } switch(signal){ case 0: printf("緑、今日も平和だ"); break; case 1: printf("黄、危ない予感"); break; case 2: printf("赤、逃げろー!"); break; } }
読ませる側や保守・運用を考えない
パワー系PGの臭いがする!!
enumを使ったときの実装
#include <stdio.h> enum Signal{ Green, Yellow, Red, }; int main(void){ int warningRate; //2桁入力【例:36】 scanf("%02d", &warningRate); enum Signal s; if(warningRate < 50){ s = Green; } else if(warningRate < 75){ s = Yellow; } else{ s = Red; } switch(s){ case Green: printf("緑、今日も平和だ"); break; case Yellow: printf("黄、危ない予感"); break; case Red: printf("赤、逃げろー!"); break; } }
【パワー系PG】ハードコーディングはやめてDBMSを使いたい
あなたのプログラム、「ハードコーディング」してませんか?
ハードコーディングとは簡単に言うと以下のような感じ
内容としては業務データをプログラムで作っている処理。
int input; scanf("%d", &employeeNo); if(employeeNo< 1&& employeeNo> 100){ //①の業務データ } else if(employeeNo< 101 && employeeNo> 200){ //②の業務データ } else if(employeeNo< 201 && employeeNo> 300){ //③の業務データ } else if(employeeNo< 301 && employeeNo> 400){ //④の業務データ } else if(employeeNo< 401 && employeeNo> 500){ //⑤の業務データ } else if(employeeNo< 501 && employeeNo> 600){ //⑥の業務データ } else if(employeeNo< 601 && employeeNo> 700){ //⑦の業務データ } else if(employeeNo< 701 && employeeNo> 800){ //⑧の業務データ }
あっ、パワー系PGが書いたコードか!!!
さて、上のコードで気になるのが何点か。
- 1とか100とか101とか200って何ぞ??
- コード量にパワーを感じる
コードを書いていない側からすると
色々気になってくるコードだね。
卒業する方法
- 1とか100とか101とか200って何ぞ??
- コード量にパワーを感じる
仰る通りです。
パワー感じますよね。
1とか100とか101とか、パッと見、数値の意味がわからんちんですな。
そもそも、
業務データを取り扱うときは
プログラムにガリガリ書くべきではないです。
まあ、当たり前の話です。
業務データを取り扱うときは
プログラムに直接持たせるのではなく、
DBMS(データベース管理システム)を使いましょう。
言語とDBMSを連携するには相応のライブラリや
フレームワークを流用するのが筋です。
DBMSも、色々種類があるので何か自分に合うものを探すと良いです。
例えば「C言語とMySqlの連携」を例に挙げるなら、
以下の記事辺りを参考にしてください。
d.hatena.ne.jp
ぐぐればいくらでも出ます。
あと、データベースにアクセスするためには
SQL文というのを覚えないといけません。
まあ単純な文なら一瞬で覚えられます。
cfm-art.sakura.ne.jp
書き方のイメージとして以下のような形式。
SELECT [営業所種別] FROM [従業員テーブル] WHERE [従業員番号]
SQL文はライブラリやフレームワークによって
プログラム内に記載できます。
例えば、WHEREの後には条件を記載するのですが、
条件に先ほど定義したemployeeNoを渡すこともできます。
※その場合はemployeeNoに対するレコードをDBからもらえます。
ですので、
データベースから引っこ抜いた営業所種別のレコードをそのまま流用すれば
プログラムとしては数行で済むわけです。
業務データはDBMSを使おう。