*で囲まれた中を塗りつぶす

***********************************************************
******   ******************************************  ******
*****    *****************************************    *****
*****   ******                         ************    ****
****  *******                               *******     ***
******                                     *********    ***
***                                 ***************     ***
***                                    **********       ***
**************                                   **    ****
******  ***************    ************* ***********  *****
*****     ***********   ***************    ****************
****       ************   **********     ******************
***          ************ **************   ****************
*****       ************* **************** ****************
*******    **************        ********   ***************
********   ***********                *****    ************
*********  ****                **************   ***********
*******      ********                    *****  ***********
********    ****************        ***********************
***********************************************************
*で囲まれた中を塗りつぶす基本的な VC++ の「再起関数」のプログラムです。

前田稔(Maeda Minoru)の超初心者のプログラム入門

プログラムの説明

  1. ソースプログラムです。
    ファイル名 説明
    Paint.cpp 塗りつぶし
  2. 図形ツールの塗りつぶしのように「*で囲まれた中の空白を、全て*に置き換える」プログラムを作成します。
    元のデータは二次元の文字列で定義していますが、単純な矩形ならともかくページ先頭のように細かく入り組んだ図形では ループしようにも方法がありません。
    このような時に威力を発揮するのが「再起関数」で、完成したプログラムは驚くほどスマートに仕上がります。 (^_^;
  3. 新規プロジェクトから、空の Console Application を作成して、次のファイルをプロジェクトに加えて下さい。
  4. 塗りつぶす元のデータの定義です。
        /*★ 内部を塗りつぶす     前田  稔 ★*/
        #include <stdio.h>
        #include <conio.h>
        char    t[20][60]=
        //  "....:....1....:....2....:....3....:....4....:....5....:....6",
        {   "***********************************************************",  //1
                      ページ先頭の図形を参照して下さい
            "***********************************************************"   //20
        };
        
  5. main() 関数では、起点となる座標を指定して paint(20,5) 関数を呼ぶだけです。
    20,5 の座標を起点として*で囲まれた範囲の空白が*に置き換わります。
        //★ MAIN PROGRAM
        int  main()
        {   int     y;
    
            paint(20,5);
            for(y=0; y<20; y++)     puts(t[y]);
            getch();
            return(0);
        }
        
  6. 内部を塗りつぶす paint() 関数ですが、驚くほど簡単でしょう。
    x,y の座標が '*' で無ければ '*' をセットして隣を調べます。
    もし '*' をセットしないと、進んだ先から戻ってきて、再起の閉ループに陥ります。
    paint() 関数の中から paint() 関数を呼び出していることに注目して下さい。
        //内部を塗りつぶす
        void  paint(int x,int y)
        {
            if (t[y][x]=='*')   return;
            t[y][x]= '*';
            paint(x+1,y);
            paint(x-1,y);
            paint(x,y+1);
            paint(x,y-1);
        }
        
  7. プログラムを実行して結果を確かめて下さい。

【メモ】

  1. 再起の考え方はとっつき難く、慣れるまで大変でしょうがその威力は抜群でぜひ身に付けたいものです。
    私の経験では「ループは全て再起」になり、完成したプログラムは非常にスマートです。 (^_^;
  2. 再起にとりつかれると何でも再起で処理したくなりますが多用は禁物です。
    作成した本人はともかく、論理の流れが他人には非常にわかり難いのです。
    試しに 順列組み合わせを表示 の論理的な流れを考えてみて下さい。
  3. 再起処理は、しっかりした構想のもと「処理できる確信」を持ってプログラムして、思い通りの結果が出て始めて その考え方が正しいことが証明されるのです。
    正しく動かない再起プログラムほど始末に悪いものはありません。
    再起処理のプログラムは、自分の責任で完成させる力を身につけてから挑戦して下さい。
  4. 再起プログラムの構成。
    1. 再起もループと同じように初期値の設定が必要です。
    2. 一般的に再起関数の先頭で、終了条件を判定して関数からリターンします。
    3. 「何か処理をして」関数を再起コールします。
      このとき次の条件を満たすことが必要です。
      • 関数を繰り返し呼び出すことにより「処理が進展する」こと。
      • 関数を繰り返し呼び出すことにより「やがて終了条件に達する」こと。
    4. 再起コールする前に「処理する」方法と、ワンレベル再起関数から戻ってから「処理する」方法があります。
      コールする前に処理する代表的なプログラムは最大公約数を求めるプログラムで、再起関数から戻ってから処理する 代表的なプログラムは階乗を求めるプログラムです。
      今回のプログラムも先に*を格納してから再起コールするので、コールする前に処理するに属します。
  5. 再起関数の代表は「リスト処理」で、その中でも LISP 言語は芸術的と思われるほどスマートです。
    機会があればぜひ挑戦してみて下さい。

超初心者のプログラム入門(C/C++)