🐈

行単位依存関係に基づくコードメトリクスの定義(案)

に公開

1. はじめに

行同士の依存関係を考える という記事を誰にでもわかりやすいような形で書いたが、この記事では行単位の依存関係の視点からコードの複雑さを定量的に評価するためのコードメトリクスを定義する。
依存関係を有向グラフとしてモデル化し、その構造に基づいて評価を行う。

ただし、このドキュメントは現状あくまでツール制作を目的としているため、重みづけのような恣意的な操作の詳細や妥当性は一旦考えていない。
(つまり、データ収集後にチューニングするという前提

2. 基本定義

ファイル F を行の列 L = {l_1, l_2, \ldots, l_n} とする。ここで n は総行数である。

依存関係とは、ある行 l_j が別の行 l_i の内容や結果に影響を受ける関係を指す。有向グラフのエッジとして表される。
依存の種類としては主に次のようなものがある。

  • データ依存:変数や定数の値の利用・参照に基づく依存
  • 制御依存:条件分岐やループの制御構造に基づく依存
  • 名前解決依存:関数や型の定義・参照に基づく依存

この記事では詳細な種類の区別はせず、単純に「l_jl_i に依存している」として扱う。なお、依存元の行は依存先の行より前に位置すると仮定し、順序付きの依存関係 (l_i, l_j) とする。

ファイル内のすべての依存関係の集合を D = {d_1, d_2, \ldots, d_m} と定義する。

3. コアメトリクス

依存関係の複雑さを定量化するため、以下の指標を定義する。

3.1. 依存関係の数

ファイル内の依存関係の総数。多いほど接続が多く、複雑である可能性が高い。

\text{TotalDependencies}(F) = |D|

3.2. 依存距離コスト

依存元と依存先の行番号の差を距離とし、その二乗和をファイル全体の距離コストとする。

単一の依存関係 d = (l_i, l_j) に対して、

\text{Dist}(d) = j - i

ファイル全体の距離コストは、

\text{TotalDistanceCost}(F) = \sum_{(l_i, l_j) \in D} (j - i)^2

3.3. 依存ツリーの複雑さ

各行の依存関係チェーンの複雑さを評価する。

3.3.1. 依存深さ

l_k の深さは、l_k に終端を持つ依存チェーンの最大長である。

\text{Depth}(l_k) = 1 + \max\left(\{ \text{Depth}(l_j) \mid (l_j, l_k) \in D \} \cup \{0\}\right)

ファイル内の最大依存深さは最も複雑な依存チェーンを示す。

\text{MaxDepth}(F) = \max_{l_k \in L} \text{Depth}(l_k)

3.3.2. 推移的依存関係のサイズ

l_k の推移的依存関係集合のサイズは、l_k が直接または間接的に依存する行数である。

まず、直接依存する行の集合を

\text{Dependencies}(l_k) = \{ l_j \mid (l_j, l_k) \in D \}

と定義し、再帰的に推移的依存関係集合を

\text{TransitiveDependencies}(l_k) = \text{Dependencies}(l_k) \cup \left( \bigcup_{l_j \in \text{Dependencies}(l_k)} \text{TransitiveDependencies}(l_j) \right)

と定義する。

そのサイズは

\text{Size}(l_k) = |\text{TransitiveDependencies}(l_k)|

ファイル内全行の平均値は依存の全体的な絡み具合を示す。

\text{AvgTreeSize}(F) = \frac{1}{n} \sum_{k=1}^{n} \text{Size}(l_k)

4. 全体のファイル複雑度スコア

ファイルの複雑度を一つのスコアにまとめるため、コアメトリクスに重み w_c, w_d, w_t をつけて合算する。

\text{FileComplexity}(F) = w_c \cdot \text{TotalDependencies}(F) + w_d \cdot \text{TotalDistanceCost}(F) + w_t \cdot \text{MaxDepth}(F)

異なるファイルサイズを比較する際には正規化も考えられるが、ここでは省略する。

5. 例

以下のコードを考える。

const a = 3;      // l_1
const b = a + 1;  // l_2
const c = b * 2;  // l_3
console.log(c);   // l_4
  • 依存関係 D = \{(l_1, l_2), (l_2, l_3), (l_3, l_4)\}

  • \text{TotalDependencies}(F) = 3

  • \text{TotalDistanceCost}(F) = (2-1)^2 + (3-2)^2 + (4-3)^2 = 1 + 1 + 1 = 3

  • 各行の依存深さ

    • \text{Depth}(l_1) = 1
    • \text{Depth}(l_2) = 1 + \text{Depth}(l_1) = 2
    • \text{Depth}(l_3) = 1 + \text{Depth}(l_2) = 3
    • \text{Depth}(l_4) = 1 + \text{Depth}(l_3) = 4
  • 最大依存深さ

    • \text{MaxDepth}(F) = 4
  • 各行の推移的依存関係のサイズ

    • \text{Size}(l_1) = |\emptyset| = 0
    • \text{Size}(l_2) = |\{l_1\}| = 1
    • \text{Size}(l_3) = |\{l_2, l_1\}| = 2
    • \text{Size}(l_4) = |\{l_3, l_2, l_1\}| = 3
  • 平均推移的依存関係サイズ

    • \text{AvgTreeSize}(F) = \frac{0 + 1 + 2 + 3}{4} = 1.5

6. おわりに

本モデルは依存関係を行単位で捉え、複雑さを定量化する枠組みを提供する。
シンプルに距離を行番号差で計算し、評価指標の基盤とした。
今後、ツール化して実際のコードベースでの有効性を検証することが期待される。

GitHubで編集を提案

Discussion