全知全能を目指す人のありがたい雑記

何かしら意味のあるありがた~い話か、意味のない雑念だらけの日記を書く予定です。

電球が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;
    }
}

何が言いたいか

int signal;って変数に
0とか1とか2って入れてるけど、
signalが0だと何なの?signalが1って何?2って?

見ただけじゃあ判断ができない。
0や1や2である意味は、
後続の処理から推測するしか方法がない。

何でもかんでもintやstringで解決する輩が一定多数いるので
気になって仕方が無い。

あと、余談だがenum⇔intの相互変換もできる。

以上

enumっていう数値を名前付き定数として
表現できるものがあるんだから使おうぜ~。。

【パワー系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を使おう。