策略、順序とテスト-《狂人C》の練習問題の解答16(第3章の練習問題6)

8186 ワード

テーマ:6.ある人は12品脱のビールを1本持っていて、8品脱と5品脱の容器があります.(0)12品の脱酒瓶内の酒を8品の脱酒瓶に注ぎ、満タンになるまで、(1)8品の脱酒瓶内の酒を5品の脱酒瓶に注ぎ、満タンになるまで、(2)5品の脱酒瓶内の酒を12品の脱酒瓶に注ぎ、満タンになるまで、(3)8品の脱酒瓶内の酒を5品の脱酒瓶に注ぎ、満タンになるまで;(4)12品脱の容器に8品脱の容器を入れ、満タンになるまで、(5)8品脱の容器に酒を5品脱の瓶に入れ、満タンになるまで.(6)5品脱容器内の酒を12品脱瓶に注ぎ、注ぎ終わるまで.この過程をプログラミングしてシミュレーションし、最後に各容器内にどれだけ酒が残っているかを出力する.この問題は難しくないが、初心者にとってできることは容易ではない.この問題を正確に、効率的に、筋道立てて完成させるコードは、本稿で議論するテーマである.何事にもポリシーがあり、コードを書くのもそうです.良い戦略はコードを優雅に余裕を持って仕事を半分にすることができて、悪い策略は人を忙しくさせて、仕事を半分にして甚だしきに至っては仕事を半分にして、甚だしきに至っては仕事を半分にして返すことができません.経験によると、テーマを手に入れて考えずに突然書き始め、最後のコードの品質は通常優れていないのは無謀であることが明らかになった.逆に,コードを書く前に熟考し,後で動くことが良質なコードの必要前提である.本テーマのステップは多いが,「満タン」と「満タン」にほかならず,この2つのステップを正確にシミュレートすることは,プログラムの正確性を保証する前提である.大きな問題をいくつかの小さな問題に分解し、小さな問題を一つ一つ破壊することがコード作成の道である.だから優秀なコードの工夫はちょうどコードの外にある.書く前に問題に対する思考と分解は、無効のように見えますが、コード自体よりも重要です.   2.善積小勝は問題を小さな問題に分解した後、直面する最も重要な問題は小さな問題を解決するコードを書くことだ.多くの人がそれを軽視し、最後のコードは無理に使えても、傷だらけの上に無数のパッチを打っただけで、このようなコードは優秀なコードとは言えない.このような短視の仕事の方法は最も基本的な常識を無視して、あなたは品質の悪い部品で品質の抜群の車に組み立てることはできません.(業界関係者によると、国産自動車はこのように生産されている)だから、優れたコードを書くには、「善を以て小さくしてはならず、悪を以て小さくしてはならない」ことを理解しなければならない.この道理を実践しようと努力する.まずA容器からB容器に入れて満タンにするコードを書きます.   3.抽象プログラマーを学ぶには抽象を学ばなければならない:抽象的に問題を提起し、抽象的に問題を解決しなければならない.このような能力が欠けていると、12、8、5のような具体的な数値にコードを書くだけでは、優れたコードは永遠に書けない.問題を解決するよりも、正確に問題を提起することが重要だ.実際にプログラムを作成する過程で、プログラマーは絶えず自分で自分に問題を提起し、それから解決しなければならない.問題を正しく提起できなければ、正しく解決することはできない.A容器からB容器を入れて満タンにする問題:もし、A容器の容積はV_Aリットル、最初はビールC_AリットルB容器の容積はV_Bリットル、最初はビールC_Bリットル.そしてV_A、C_A、V_B、C_Bはすべて整数であり、C_を満たすA+C_B ≥ V_B .A容器内のビールをB容器に入れて満タンになるまで注ぎ、その後、2容器内のビールがどれだけあるかを尋ねる.   4.順序は抽象的な問題を解決するために抽象的に問題を解決する必要があるが、コンピュータは具体的な数値に対してしか計算できない.従って、以下のコードは、いくつかの具体的な数値を仮定し、これらのデータをシンボル定数によってプログラムに導入して問題を具体化する.これらのシンボル定数がもたらすメリットの1つは、テストの組織を容易にすることです.コードの正確性には底があるのは難しいが、テストされていないコードは心の中でさらに底がない.以下のコードで解決される具体的な問題は、A容器の容積が12リットルであれば、最初はビールが12リットルある.B容器の容積は8リットルで、最初はビールが0リットルありました.A容器内のビールをB容器に入れて満タンになるまで注ぎ、その後、2容器内のビールがどれだけあるかを尋ねる.
/*
,A V_A , C_A ;
B V_B , C_B 。
V_A、C_A、V_B、C_B , C_A+C_B≥V_B。
A B , 。
*/
#include <stdio.h>
#include <stdlib.h>

#define V_A 12U
#define V_B 8U

//
#define C_A 12U
#define C_B 0U


int main( void )
{
unsigned beer_A = C_A ,
beer_B = C_B ;

// A B ,
beer_B = V_B ; //B // :
beer_A -= ( V_B - beer_B ) ; //A


printf("%u,%u
", beer_A , beer_B );

system("PAUSE");
return 0;

}

テストの結果は理想的ではありません.このプログラムの実行後の出力は:12,8初心者によく見られるエラーです.順序を無視します.実際にコードが表すコンピュータに要求されるのは演算だけでなく,演算の順序もあり,演算が正しい順序に従わなければプログラムが正しいはずがない.上記のコードの誤りは、まず「beer_B=V_B」という付与演算が行われ、beer_Bに元々格納されていた値は、新しい値が格納されたことによって「消失」し、その後「(V_B-beer_B)」を計算すると、B容器にビールがどれだけ入ったか、A容器からビールがどれだけ出たかではなくなります.このプロセスを正しくシミュレートするには、まず2番目の付与演算を行う必要があります.プログラミングでは、変数が異なる時点で格納される値の意味が異なり、初心者は変数の値が変化した後も変数の値を以前の値の意味と勘違いすることが多いことに注意してください.修正後のコードは
/*

 ,A      V_A ,     C_A ;

B      V_B ,     C_B 。

 V_A、C_A、V_B、C_B    ,   C_A+C_B≥V_B。

 A        B        ,              。

*/

#include <stdio.h>

#include <stdlib.h>



#define V_A    12U

#define V_B     8U



//    

#define C_A  12U

#define C_B   0U



 

int main( void )

{

   unsigned beer_A = C_A , 

            beer_B = C_B ;  

   

   // A       B  ,    

   beer_A -= ( V_B - beer_B ) ;  //A        

   beer_B  =   V_B ;             //B     



   printf("%u,%u
", beer_A , beer_B ); system("PAUSE"); return 0; }

修正後のプログラムの実行結果は,4,8と予想通りであった.さらに、他のいくつかのデータを用いて試験し、試験の一般的な原則は、2つの容器中のビールの様々な境界値と中間値(12,11および8,2,0)の様々な合理的な組み合わせであるべきである:#define C_A 12 U#define C_B 2 U
#define C_A  12U#define C_B   8U
#define C_A  11U#define C_B   0U
#define C_A  11U#define C_B   2U
#define C_A  11U#define C_B 8 Uのテスト結果はすべて正しいので、これらのテストはプログラマのコードに対する自信を高めることができます.ここから、シンボル定数の大きな利点は、テスト時にコード部分を変更することなく、コードの変更に新しいエラーを導入する可能性があることです.   5.図面に基づいて瓢箪を描くと、前の「満タン」コードに基づいて「満タン」コードを与え、テストを組織しやすくなります.
/*

 ,A      V_A ,     C_A ;

B      V_B ,     C_B 。

 V_A、C_A、V_B、C_B    ,   C_A+C_B≤V_B。

 A        B          ,              。

*/

#include <stdio.h>

#include <stdlib.h>



#define V_A     8U

#define V_B    12U



//    

#define C_A   8U

#define C_B   0U



//#define C_A  8U   //    

//#define C_B  2U



//#define C_A   6U

//#define C_B   0U



//#define C_A   6U

//#define C_B   2U



//#define C_A   0U

//#define C_B  12U



//#define C_A   0U

//#define C_B   8U



//#define C_A   0U

//#define C_B   0U



 

int main( void )

{

   unsigned beer_A = C_A , 

            beer_B = C_B ;  

   

   // A       B  ,    

   beer_B += beer_A ;  //B    A   

   beer_A  = 0 ;       //A        



   printf("%u,%u
", beer_A , beer_B ); system("PAUSE"); return 0; }

   6.プログラムを完成して前の準備ができたら、簡単に問題の提出した問題を簡単に解決することができます.まず、コードのフレームワークを示します.
#include <stdio.h>

#include <stdlib.h>

//   

#define V_12  12U 

#define V_8    8U 

#define V_5    5U 



//   

#define C_12 12U 

#define C_8   0U 

#define C_5   0U 

 

int main( void )

{

   unsigned beer_12 = C_12 , 

            beer_8  = C_8  ,

            beer_5  = C_5  ;  //          



   // 12         8     ,    ;

   // 8         5     ,    ;

   // 5         12    ,    ;

   // 8         5     ,    ;

   // 12         8     ,    ;

   // 8         5    ,    ;

   // 5         12    ,    ;



   printf("%u,%u,%u
", beer_12 , beer_8 , beer_5 ); system("PAUSE"); return 0; }

「倒満」と「倒完」のコードが完了したため:beer_A -= ( V_B - beer_B ) ;//A容器から倒されたbeerを減算B  =   V_B ;//B容器がいっぱいとbeer_B += beer_A ;//B容器にA容器を加えたbeer_A  = 0 ;//Aコンテナは空になっているので、この2つのコードをそれぞれコードに貼り付ける必要がある場所にコピーして少し編集すればいいです.最後のコードは次のとおりです.
#include <stdio.h>

#include <stdlib.h>

//   

#define V_12  12U 

#define V_8    8U 

#define V_5    5U 



//   

#define C_12 12U 

#define C_8   0U 

#define C_5   0U 

 

int main( void )

{

   unsigned beer_12 = C_12 , 

            beer_8  = C_8  ,

            beer_5  = C_5  ;  //          



   // 12         8     ,    ;

   beer_12 -= ( V_8 - beer_8 ) ;  

   beer_8   =   V_8 ;                 

   // 8         5     ,    ;

   beer_8  -= ( V_5 - beer_5 ) ;  

   beer_5   =   V_5 ;             

   // 5         12    ,    ;

   beer_12 += beer_5 ;  

   beer_5   = 0 ;           

   // 8         5     ,    ;

   beer_5  += beer_8 ;  

   beer_8   = 0 ;             

   // 12         8     ,    ;

   beer_12 -= ( V_8 - beer_8 ) ;  

   beer_8   =   V_8 ;             

   // 8         5    ,    ;

   beer_8  -= ( V_5 - beer_5 ) ;  

   beer_5   =   V_5 ;             

   // 5         12    ,    ;

   beer_12 += beer_5 ;  

   beer_5   = 0 ;          



   printf("%u,%u,%u
", beer_12 , beer_8 , beer_5 ); system("PAUSE"); return 0; }

実行結果:6,6,0