2013/12/27

慣性モーメントの計算の改善案

既に決まったルーチンをコンピュータにやらせるのはいい考えですね! ただ、ソースコードが以下の点で保守がしづらいと思われますので、改善案を示しておきます。

1. グローバル変数が多数の関数内で参照 & 書き換えがされていて、修正が容易でないこと。
2.  同じ変数について、ifでの分岐があちこちに散在していてバグの元となること。
3. 「3.14」の精度が足りない場合がある。それ以前にプログラム中に出てくる定数はマジックナンバーと呼ばれ、修正が大変になるので事前に定義しておく。

#include <stdio.h>

#define _USE_MATH_DEFINES
#include <math.h>

double chouhoukei(double* ref_m, double ro);
double nagaibou(double* ref_m, double ro);
double enban(double* ref_m, double ro);
double enchuu(double* ref_m, double ro);
double entou(double* ref_m, double ro);
double kyuu(double* ref_m, double ro);
double kyuukaku(double* ref_m, double ro);

static const double g_ro[3] = { 2.7 / 1000000, 7.9 / 1000000, 1.19 / 1000000 };
static const double (*f[7])(double*, double) = { chouhoukei, nagaibou, enban, enchuu, entou, kyuu, kyuukaku };

int main(void) {
   int i;
   float t0 = 0;

   printf("任意の軸(機力の教科書p251の図参照)まわりの慣性モーメントJ[kgcm^2]\n");

   for(i = 1; ; i++) {
      // 使う変数の領域を考えて極量スコープを小さく
      int u, n;
      double e, m, ro, t;

      printf("部材を番号で選んでください\n0.アルミ,1.ステンレス,2.アクリル,3.その他(密度既知)\n4.その他(質量既知),5.終了\n");
      scanf("%d", &u);

      // 密度既知の場合
      if(u >= 0 && u <= 2) {
         ro = g_ro[u];
      }
      else if(u == 3) {
         ro = 0;
         printf("密度[kg/m^3]を入力してください\n");
         scanf("%lf", ro);
         ro /= 1000000000;
      }
      else if(u == 5) {
         break;
      }

      printf("部品%dの形を番号で選んでください.\n1.長方形板,2.長い棒,3.薄い円盤,4.円柱,5.円筒形\n6.球,7.球殻,8その他(慣性モーメント既知)\n", i);
      scanf("%d", &n);

      // 質量既知の場合
      if(u == 4) {
         printf("質量[g]を入力してください\n");
         scanf("%lf", &m);
         m /= 1000;
         ro = 0;
      }
      else {
         m = 0;
      }

      // 慣性モーメントが既知の場合
      if(n == 8) {
         printf("\n部品%dの慣性モーメント[kgmm^2]を入力してください\n\n", i);
         scanf("%lf", &t);
      }
      // 想定外の値が入力されたらエラーを表示
      else if(n < 0 || n > 8) {
         // printf("nは1から8の範囲で入力して下さい\n");
         continue;
      }
      // elseを使う場面
      else {
         printf("重心の回転軸からの距離[mm]を入力してください\n");
         scanf("%lf", &e);

         // 同じ処理はまとめる
         t = (*f[n - 1])(&m, ro) + e * e * m;

         printf("\n部品%dの質量=%lf[kg]\n", i, m);
         printf("部品%dのJ=%lf[kgcm^2]\n\n", i, t / 100);
      }

      t0 += t / 100;
   }

   printf("\n全体の慣性モーメント\nJ=%lf[kgcm^2]", t0);

   return 0; // 正常の場合はmain関数は0(EXIT_SUCCESS)を返す
}

double chouhoukei(double* ref_m, double ro) {
   int p;
   double a, b, tt, m;

   printf("回転軸を選んでください\n0.x  1.y  2.z\n");
   scanf("%d", &p);

   printf("横の長さ[mm]を入力してください\n");
   scanf("%lf", &b);

   printf("縦の長さ[mm]を入力してくだいさい\n");
   scanf("%lf", &a);

   m = *ref_m;

   if(m == 0) {
      printf("厚さ[mm]を入力してください\n");
      scanf("%lf", &tt);

      m = ro * (a * b * tt);
      *ref_m = m;
   }

   switch(p) {
   case 0:
      return a * a * m / 12;
   case 1:
      return b * b * m / 12;
   case 2:
      return ((a * a + b * b) * m) / 12;
   }

   return 0;
}

double nagaibou(double* ref_m, double ro) {
   double l, tt, m;

   printf("長さ[mm]を入力してください\n");
   scanf("%lf", &l);

   m = *ref_m;
   if(m == 0) {
      printf("直径[mm]を入力してください\n");
      scanf("%lf", &tt);

      m = ro * (M_PI * tt * tt / 4) * l;
      *ref_m = m;
   }

   return m * l * l / 12;
}

double enban(double* ref_m, double ro) {
   double r, tt, m;
   int p;

   printf("回転軸を選んでください\n0.x  1.y  2.z\n");
   scanf("%d", &p);

   printf("半径[mm]を入力してください\n");
   scanf("%lf", &r);

   m = *ref_m;
   if(m == 0) {
      printf("厚さ[mm]を入力してください\n");
      scanf("%lf", &tt);

      *ref_m = ro * M_PI * r * r * tt;
      m = *ref_m;
   }

   switch(p) {
   case 0:
      return r * r * m / 4;
   case 1:
      return r * r * m / 4;
   case 2:
      return r * r * m / 2;
   }

   return 0;
}

double enchuu(double* ref_m, double ro) {
   int p;
   double r, l, m;
   
   printf("回転軸を選んでください\n0.x  1.y  2.z\n");
   scanf("%d", &p);

   printf("半径[mm]を入力してください\n");
   scanf("%lf", &r);

   printf("長さ[mm]を入力してくだいさい\n");
   scanf("%lf", &l);

   m = *ref_m;
   if(m == 0) {
      m = ro * M_PI * r * r * l;
      *ref_m = m;
   }

   switch(p) {
   case 0:
      return (r * r / 2 + l * l / 12) * m;
   case 1:
      return (r * r / 2 + l * l / 12) * m;
   case 2:
      return r * r * m / 2;
   }

   return 0;
}

double entou(double* ref_m, double ro) {
   int p;
   double r, l, tt, m;

   printf("回転軸を選んでください\n0.x  1.y  2.z\n");
   scanf("%d", &p);
   printf("半径[mm]を入力してください\n");
   scanf("%lf", &r);

   // これ忘れているんじゃないか?
   printf("長さ[mm]を入力してくだいさい\n");
   scanf("%lf", &l);
   // ここまで

   m = *ref_m;
   if(m == 0) {
      printf("筒の厚さ[mm]を入力してください\n");
      scanf("%lf", &tt);

      m = ro * M_PI * r * r * l - M_PI * (r - tt) * (r - tt) * l;
      *ref_m = m;
   }

   switch(p) {
   case 0:
      return (r * r / 2 + l * l / 12) * m;
   case 1:
      return (r * r / 2 + l * l / 12) * m;
   case 2:
      return r * r * m;
   }

   return 0;
}

double kyuu(double* ref_m, double ro) {
   double r, m;
   
   printf("半径[mm]を入力してください\n");
   scanf("%lf", &r);

   m = *ref_m;
   if(m == 0) {
      m = ro * 4 * M_PI * r * r * r / 3;
      *ref_m = m;
   }

   return 2 * m * r * r / 5;
}

double kyuukaku(double* ref_m, double ro) {
   double r, tt, m;

   printf("半径[mm]を入力してください\n");
   scanf("%lf", &r);

   m = *ref_m;
   if(m == 0) {
      printf("核の厚さ[mm]を入力してください\n");
      scanf("%lf", &tt);

      m = ro * 4 * M_PI * r * r * r / 3 - 4 * M_PI * (r - tt) * (r - tt) * (r - tt) / 3;
      *ref_m = m;
   }

   return 2 * r * r * m / 3;
}

1 件のコメント:

  1. 改善案ありがうございます!
    参考にしてできるかぎり改善しようと思います!

    返信削除