見出し画像

関数型プログラミング事始め (40) 評価(2)

関数型プログラミングがはじめての方へ贈る入門の書
前節:評価(1) 次節:評価(3)
参考書:
・五味 弘「はじめてのLisp関数型プログラミング」技術評論社(2016)
・大山口 通夫、五味 弘「プログラミング言語論」コロナ社(2008)
・五味 弘「関数型プログラミングと数学(ITと数学)」技術評論社(2021)

関数型プログラムの評価はどこをどのように見ればいいのでしょうか。今回も前回と同様に通常のプログラムの評価方法の続きを見ていきます。前回は関数のサイズや引数の個数、グローバル変数、ローカル変数の個数について見てきました。今回は計測が困難な指標であるサイクロマティック複雑度や結合度、凝集度を見ていきます。

4.2 定量的な計測が困難な評価(通常のプログラム版)

プログラムを評価するとき、関数のサイズや引数個数、グローバル変数、ローカル変数などの定量的な数値を持って評価することは重要です。そしてこの定量的な数値を出すためには、計測が低コストで行えることが必要です。

しかしプログラムの評価には有効でも、定量的な数値を出すのが難しい評価観点もあります。このための計測基準(メトリクス)を見ていくことにします。

(1) サイクロマティック複雑度

サイクロマティック複雑度(循環的複雑度、cyclomatic complexity)はマケイブ(McCabe)によって提唱されたプログラムの複雑度に関する評価観点です。

サイクロマティック複雑度(循環的複雑度)の定量的計測をした数値(サイクロマティック数)は、関数などのプログラム断片において、その制御フローの閉路数(に1を加えた数)に相当します。簡単に言えば、分岐分や繰り返し文が多いかどうかでプログラムの良し悪しを評価する数値になります。

このサイクロマティック数は、コードメトリクスツールで計測できる定量的な観測値になります。逆に言えば、メトリクスツールがないと計測が困難なものです。

サイクロマティック数の閾値は10とも20とも25とも言われています。これはプログラミング言語によって異なり、さらに再帰プログラミングか繰り返しプログラミングかでも大きく異なります。

繰り返しプログラミングはサイクロマティック数は大きくなりがちです。そのため閾値として、10では小さいでしょう。でも25は大きいのでは・・・と思います。

一方、再帰プログラミングはサイクロマティック数は小さくなります。関数単位では、サイクロマティック数は閉路がない1が理想です。

(2) 結合度

結合度(coupling)は関数などのプログラム断片の結合の度合いを見るための指標です。悪い密結合から良い疎結合の順でレベルを見ていくと、以下のようになります。


Level 1. 内容結合(content coupling)
関数内部の内容を別の関数が直接操作できる結合。
副作用がいつ起こるかわからない最低な結合。FORTRANのCOMMON領域などがこれに当たる。

Level 2. 共通結合(common coupling)
複数の関数が共通のグローバル変数などを操作する結合。
グローバル変数はバグを引き起こす害悪(けれど便利な必要悪)であることを認識する必要がある。

Level 3. 外部結合(external coupling)
グローバル変数などのメモリ上でない外部のデータソース(データベース、デバイス、ネットワーク、画面など)を介する結合。
この結合は外部のデータソースは特殊で、その個数が限定されていることもあり、仕方ない面がある結合である。この結合により副作用が生じることに留意して使うのが吉。

Level 4. 制御結合(control coupling)
制御フラグやスイッチなどの関数内部の制御情報を受け渡す結合。
この結合により、コピペプログラミング(プログラムをコピーして、少しだけ修正するプログラミング)を防ぐ効果がある。しかしプログラムは複雑になるのでデータ主体のプログラムにするのが望ましい。

Level 5. スタンプ結合(stamp coupling)
オブジェクトや構造型などの複合データの一部を受け渡す結合。
この結合は意図していない複合データの別の箇所を操作してしまう危険性があり、またコピーが必要になったときの効率も悪い。しかし、この点に気を付けていれば、多少使ってもよい(謎の上から目線)。

Level 6. データ結合(data coupling)
引数のみでやりとりをする結合。
関数型プログラミングの基本であり、一般のプログラミングでも推奨される結合である。スタンプ結合で必要な要素データのみを引数にする結合でもある。

Level 7. メッセージ結合(message coupling)
引数のない関数呼び出しによる結合。
データの受け渡しをせずに関数だけでプログラミングするものである。ただしこれだけでプログラミングするのは困難。

Level 8. 無結合(no coupling)
関係がまったくない結合。結合していない結合。
無関係である関数を一切の結合をするべきでないのは、当たり前。


結合度としては、引数だけのデータ結合が望ましいものになります。スタンプ結合も効率が良く、作るのに面倒がないので、認めてもいいでしょう。制御結合はコピペをなくすにはいいのですが、本来的にはそのような構造になるのがおかしいのですが、リファクタリングをするのにコストが必要な場合は仕方がないでしょう。外部結合も仕方がない面があります。数が少なく、留意することができれば使ってもいいでしょう。内容結合や共通結合はもっての他です。

この結合度は、詳細にはツールで計測するのは難しいものになります。しかしグローバル変数の使用は簡単に計測できますので、それらを参考にリスク対応するのがいいでしょう。

(3) 凝集度

凝集度(cohesion)は関数などのプログラム断片の内容の結びつき(凝集)の強さを判定する順序尺度です。この凝集度を悪い順から見ていきます。


Level 1. 偶発的凝集(coincidental cohesion)
関数の内容の結びつきに意味がない結びつき。
作ったプログラマでさえ、関数の内容の結びつきを説明できない。

Level 2. 論理的凝集(logical cohesion)
論理的な意味を持って集めた結びつき。
特定のデータソースやデバイス単位で集めた関数など、プログラマにとっては意味があると思い込んでいる結びつき。プログラムの構造的には無意味。

Level 3. 時間的凝集(Temporal cohesion)
時間的に近く動作するものを集めた結びつき。
初期化処理や後始末処理などのように時間的に近く動作する機能を集めた関数。論理的凝集において時間に注目した結びつき。構造的には意味がない。

Level 4. 手続き的凝集(procedural cohesion)
順番で実行される手続きをまとめた結びつき。
順番に意味がある場合は上位のレベルの凝集度になるが、順番が偶然である場合はこの手続き的凝集度になる。この場合は構造的には意味がない。

Level 5. 通信的凝集(communicational cohesion)
同じデータを扱う関数を集めた結びつき。
オブジェクト指向プログラミングのクラスに相当するが、機能分割が進んでいない結びつき。巨大になりがちなので注意が必要。

Level 6. 逐次的凝集(sequential cohesion)
同一の入出力データの逐次的処理を集めた結びつき。
手続き的凝集度において、同一の入出力データを使うときの結びつきであり、作りやすく比較的了解性がある結びつき。

Level 7. 機能的凝集(functional cohesion)
単一の機能を実行するために集めた結びつき。
人間的に作りやすく理解しやすい結びつき。プログラミングにおける王道。


機能的凝集が最善であり、プログラミングの基本となります。1関数1機能の原則でプログラミングしていきます。
この他の凝集度を使うときには、関数名にそれとわかるネーミング、たとえば、データ名や初期化などの名前を付けるのがいいでしょう。

この凝集度は、ツールで直接計測するのは難しいものになります。間接的に計測するツールもありますが、結局は人間が手動で凝集度を発見することになります。この場合はレビューなどでのリスク指摘に役立ててください。

(予告) 4.3 関数型プログラムの評価

今回は通常のプログラムにおける定量的に計測困難な評価指標を見てきました。次回は関数型プログラム特有の評価指標を見ていくことにします。

いいなと思ったら応援しよう!

五味弘
よろしければサポートをお願いします!