[境界要素法プログラムを設計する(境界積分サブルーテイン:策定編)]


 


1.サブルーティンは必要か?


 世の中には、何でもかんでもサブルーティンにすれば良いと思ってる人がけっこういますが、自分の意見は違います。メインルーティンの作業をサブルーティン化すれば、確かにメインは綺麗に整理されてプログラムのフローがわかりやすくなります。しかし代償もあります。それは具体的な作業コードが隠蔽化(カプセル化)されて、可読性(読み解きやすさ)が悪くなるケースもあり得るからです。


 特に大規模プログラムでは何10~何100というサブルーティンが現れ(大規模だからしゃーないんだけど)、こっちのファイルを読みあっちのファイル読んで、そっちのページを・・・とやってると、最弱マシンである人間は、それだけでバグ化します(^^;)。こういう場合は、メインの作業フローを組み直すべきなんですが、では次のような例はどうでしょう?。


 


 よくサブルーティンは引数付きが推奨されます。ローカル変数が使えるので、変数競合を避けられるからです。最強マシンであるPCが、最弱マシンである人間のミスから、その最弱マシンを自動で守ってくれます。


 


 メイン冒頭でxの値を評価し、その評価によって計算の行く末を決める、計算初期値yの値が変わるとします。


 


    If x < 0 Then
      y = 0
    ElseIf (0 <= x) And (x < 1) Then
      y = 1
    Else
      y = 2
    End If

    REM yを使った計算




 


 これはサブルーティン化すべきでしょうか?。するとしたら、こんな感じになりそうです。


 


    External Function y_Setter(x)

      If x < 0 Then
        y = 0
      ElseIf (0 <= x) And (x < 1) Then
        y = 1
      Else
        y = 2
      End If

      y_Setter = y
    End Function


 メインの側は、



    If x < 0 Then
      y = 0
    ElseIf (0 <= x) And (x < 1) Then
      y = 1
    Else
      y = 2
    End If

   REM yを使った計算




・・・と非常にすっきり(^^)。ところが入力仕様が変わり、yのケース分けが1個追加され、しかもケース境界も可変になる事がわかりました。それにともなって「yを使った計算」も変更されます。こうなると、


 


    External Function y_Setter(x)

      If x < 0 Then
        y = 0
      ElseIf (0 <= x) And (x < 1) Then
        y = 1
      Else
        y = 2
      End If

      y_Setter = y
    End Function

メインの側は、

    Declare External Function y_Setter
    y = y_Setter(x)
   REM yを使った計算


です。開発現場のトップ(土木では現場代理人)はたいがい、PMと呼ばれるプロジェクト・マネージャー(Project Manager)が行います。PMは顧客要求の聞き取り(ヒアリング)も兼任する事が多いです。そのPMが良くない知らせを持ってきます。「yの出力値も可変になる」と。

 


 だいたいこの辺りからPMは、PG(プログラマー)の恨みを買い出します(^^;)


 



    Declare External Function y_Setter
    y = y_Setter(x, z1, z2, z3)  REM z1~z3はケース分けの境界値
   REM yを使った計算_変更1

    External Function y_Setter(x, z1, z2, z3)

      If x < z1 Then
        y = 0
      ElseIf (0 <= z1) And (x < z2) Then
        y = 1
      ElseIf (0 <= z2) And (x < z3) Then
        y = 2
      Else
        y = 3
      End If

      y_Setter = y
    End Function

 


 事態はさらに最悪の方向へ向かいます。「開発が終わるまで入力仕様の変更は続き、ケース分けは何ケースになるかわからない。それに対処できるようにしといてくれ」。・・・頭に血が上ったPGは、こう叫びます。


 


 「わかった。配列で渡すようにする。後で汚いとかコードが重いとか、文句言うなよ!」


 


 引数付きサブルーティンは確かに推奨されるべきものです。でも、入力仕様の変更に非常に弱いという欠点も持ちます。だからやめなさいって、そんなもん。自分ならメインで単にこうします。


 



    If x < z1 Then
      y = y0
    ElseIf (0 <= z1) And (x < z2) Then
      y = y1
    ElseIf (0 <= z2) And (x < z3) Then
      y = y2
    Else
      y = y3
    End If

   REM yを使った計算_変更2
    REM ElseIfブロックは、何個でもコピーして増やせる!


 重要なのは、最後のREM文です。たぶんy_Setterの中でも同じ事が出来る、という意見はあると思います。でも駄目です。引数並びが、どんどん重く複雑になるからです。最悪の場合、コードを管理してるんだか引数並びを管理してるんだか、わからないような事態になります。そういう「捩れプロジェクト」は後で涙の嵐です。

 もう一つは、引数並びが重く複雑なサブルーティンは内容も複雑な訳です。しかもサブルーティン化したためにコードが隠蔽され、メインと並べて一目で見渡せなくなります。最弱マシンはそこで、必ず何かを見落とします。バグ特定がしずらいため、それはバグの連鎖反応も招きかねません。


 


 でもテスト技術者(TE)って、こういうコードが嫌いなような印象を受けます(規格物好き?)。確かに綺麗なコードの方が、テストしやすいですもんね。「何でサブ化しない?」ってけっこう言われました(^^;)


 


PG「だって仕方ねぇ~だろう。PMの野郎が仕様をどんどん変えるんだから」


TE「それとコードの可読性は別だ。サブ化しないから可読性が悪い」


PG「悪いだと?。上から順番に読めば、必ずわかるコードのどこが悪い」


TE「メインに1万行も書きやがって、この馬鹿ものがぁ~!」


PG「うるせぇ~!。順番に1万行も読めない奴は、PGでもTEでもねぇ~!」


PM「開発の最終段階でサブ化したら?」


PGTE「・・・・・・」


 


2.境界要素法プログラムは?


 で、境界要素法プログラムではどうでしょう?。とりあえず問題の部分は、


 


  REM (x0y0)(x1y1)(x2y2)から、要素kの境界積分値を計算


 


でした。ここで(x0y0)は特異点座標、(x1y1)(x2y2)は一つの境界要素kの端点座標です。これらは入力部で既に確定しています。という事は、仕様変更はほとんどなさそうです。


 


 ・・・ではサブ化しますか。でも自分の考えでは、これだけでは弱いと思います。境界要素法プログラムの[計算部]をサブ化せずに全部ベタ書きしても、1000行くらいだと思います。さっきのお馬鹿PGだったら「ベタ書きしろぉ~!」というでしょう。1000行くらいなら全体を一気に見渡せると思えるからです。なぜコードを隠蔽して、見にくくするのか?。もっと強力な理由が必要です。設計における方針決定には、明白な根拠があるべきです。


 


 「境界要素番号kで境界積分を行うLoop」が動的過程だからです。前回の該当部分を見て下さい。自分は十分に長いLoopだと思います。その最大の理由は、要素-節点対応表を使って積分値の重ね合わせをやってるからです。つまりこのLoopは一種の漸化式なんですよ。漸化式って短くても、すぐ訳わかんなくなりますよね?(^^;)。そういう部分は出来るだけ綺麗に見やすくしとくべきだ、と思うからです。境界積分値を計算する、サブルーティンを作ると方針決定します。


 これは、


  1) 動的過程を制御するメタな静的構造(今はLoop)の可読性を上げて確保する.


 


事でもあります。ほとんど意識されませんが、サブルーティンを用いる最も大きな理由の一つが上記だと自分は思います(コード自体は短くても)。y_Setterはそういうものではありませんでした。


 


 あとサブルーティンを用いる一般的理由としては、当然ながら、


  2) メインが長すぎて一気に読めない(1万行は多すぎる(^^;)).


  3) 再利用性を考えた.


などです。y_Setterは、2)3)いずれにも該当しません。


 


 3)は(オブジェクト指向も含めて)サブルーティン化の大きな理由とされますが、じつはバグの発生頻度とトレードオフの関係にあります。


 一般にコードは書けば書くほどバグ確率は比例して増加します。再利用すれば一回しか書かないので、良さそうに思えます。しかしコードは隠蔽されるので、潜在バグの発生確率が上がります。それに気づかぬまま潜在バグが発覚すると、今度はシステム全体で大被害です。大抵そこら中で使い回すからですよ。大被害が出るとバグの場所はわかりやすいですけれど、運用段階に入ってた銀行のシステムなんかだと、わかりやすいでは済みません(^^;)。一番困るのが、運用段階に入ったサブルーティン間の競合です。


 


 以上は全て人間の都合です。機械にとってはどうでも良い事です。なので2)などは、人間の限界を認めた話です。人間原理最優先で行きましょう(^^)。人間原理最優先なので、以後サブルーティンと関数にはローカル変数を持てる外部手続きのみ用います。タフな機械に守ってもらいます。