<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Complexity on Tarragon</title><link>https://tarrragon.github.io/blog/tags/complexity/</link><description>Recent content in Complexity on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Mon, 24 Aug 2026 00:00:00 +0800</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/complexity/index.xml" rel="self" type="application/rss+xml"/><item><title>Cyclomatic Complexity（圈複雜度）</title><link>https://tarrragon.github.io/blog/testing/knowledge-cards/cyclomatic-complexity/</link><pubDate>Mon, 24 Aug 2026 00:00:00 +0800</pubDate><guid>https://tarrragon.github.io/blog/testing/knowledge-cards/cyclomatic-complexity/</guid><description>&lt;p>圈複雜度數的是一段程式碼裡線性獨立的執行路徑有幾條，實作上等於分支點的數量加一——每個 &lt;code>if&lt;/code>、每個迴圈條件、每個 &lt;code>case&lt;/code>、每個布林運算子的短路點各加一。它的原始用途是估算「要完全覆蓋這段程式需要幾個測試案例」，所以它跟&lt;a href="https://tarrragon.github.io/blog/testing/knowledge-cards/test-oracle/" data-link-title="Test Oracle（測試判準來源）" data-link-desc="一個測試憑什麼判定通過或失敗說不清楚、或被測對象沒有可逐例算出的正確答案時，用來定位判準的來源，以及每一種來源的射程">測試判準&lt;/a>是同一個問題的兩端：複雜度給出案例數的下界，判準決定每個案例斷言什麼。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>它量的是&lt;strong>單一函式的分支數&lt;/strong>，不量概念的完整性，也不量函式之間的耦合——跟&lt;a href="https://tarrragon.github.io/blog/testing/knowledge-cards/mutation-testing/" data-link-title="Mutation Testing（突變測試）" data-link-desc="行覆蓋率很高卻仍漏掉 bug、或要挑一個比覆蓋率難灌水的測試品質指標時，用來判斷這套測試實際擋得住什麼">突變分數&lt;/a>的分工很清楚：這裡數的是路徑有幾條，那裡數的是那些路徑上的斷言擋不擋得住行為改變。這個射程決定了它當品質閘門時的行為：把一個十分支的函式切成四個各三分支的函式，總分支數不變而每個函式都合規——指標下降而閱讀時要重建的脈絡變多。&lt;a href="https://tarrragon.github.io/blog/report/mechanical-constraints-buy-the-measured-number/" data-link-title="機械約束買到被量測的那個數字，代價落在沒被量測的維度" data-link-desc="用函式長度、圈複雜度、覆蓋率門檻這類可自動檢查的品質約束時，用來判斷達成它換到了什麼、又付出了什麼">機械約束買到被量測的那個數字&lt;/a>那張卡用的正是這個度量當實例。&lt;/p>
&lt;p>CRAP 度量把它跟覆蓋率合起來：&lt;code>CRAP(m) = C²(1 − cov)³ + C&lt;/code>，其中 &lt;code>C&lt;/code> 是圈複雜度、&lt;code>cov&lt;/code> 是該函式的覆蓋率。滿覆蓋率時 CRAP 等於圈複雜度本身，所以「CRAP 低於 4」等價於「每個函式的複雜度不超過 3」。&lt;/p>
&lt;h2 id="可觀察訊號與例子">可觀察訊號與例子&lt;/h2>
&lt;p>一個 &lt;code>if&lt;/code> 加一個 &lt;code>for&lt;/code> 加一個三元運算子的函式，複雜度是 4，完全覆蓋它至少要四個案例。同一段邏輯改用查表取代分支，複雜度會掉到 1 而行為不變——這是它與「這段程式難不難懂」脫鉤的地方：查表版可能更難懂，指標卻好看得多。&lt;/p>
&lt;p>門檻設在 10 上下是工具的常見預設，出處是原始論文的建議值而不是實證；設在 3 是特定實驗的參數，不是通用建議。看到一個複雜度門檻時要先問這個數字從哪來。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>拿它當閘門的人要同時指定它量不到的那個維度該由誰檢查。可機械執行的是&lt;strong>觸發&lt;/strong>——每個新產生的函式名都被問一次；判定那個名字對不對得回一個領域概念，仍然是人的工作，可以要求它在需求文件或領域詞彙表裡找得到對應。沒有配對這一步時，最便宜的達成方式（把違規處切碎）與真正的分解在指標上完全一樣。&lt;/p></description><content:encoded><![CDATA[<p>圈複雜度數的是一段程式碼裡線性獨立的執行路徑有幾條，實作上等於分支點的數量加一——每個 <code>if</code>、每個迴圈條件、每個 <code>case</code>、每個布林運算子的短路點各加一。它的原始用途是估算「要完全覆蓋這段程式需要幾個測試案例」，所以它跟<a href="/blog/testing/knowledge-cards/test-oracle/" data-link-title="Test Oracle（測試判準來源）" data-link-desc="一個測試憑什麼判定通過或失敗說不清楚、或被測對象沒有可逐例算出的正確答案時，用來定位判準的來源，以及每一種來源的射程">測試判準</a>是同一個問題的兩端：複雜度給出案例數的下界，判準決定每個案例斷言什麼。</p>
<h2 id="概念位置">概念位置</h2>
<p>它量的是<strong>單一函式的分支數</strong>，不量概念的完整性，也不量函式之間的耦合——跟<a href="/blog/testing/knowledge-cards/mutation-testing/" data-link-title="Mutation Testing（突變測試）" data-link-desc="行覆蓋率很高卻仍漏掉 bug、或要挑一個比覆蓋率難灌水的測試品質指標時，用來判斷這套測試實際擋得住什麼">突變分數</a>的分工很清楚：這裡數的是路徑有幾條，那裡數的是那些路徑上的斷言擋不擋得住行為改變。這個射程決定了它當品質閘門時的行為：把一個十分支的函式切成四個各三分支的函式，總分支數不變而每個函式都合規——指標下降而閱讀時要重建的脈絡變多。<a href="/blog/report/mechanical-constraints-buy-the-measured-number/" data-link-title="機械約束買到被量測的那個數字，代價落在沒被量測的維度" data-link-desc="用函式長度、圈複雜度、覆蓋率門檻這類可自動檢查的品質約束時，用來判斷達成它換到了什麼、又付出了什麼">機械約束買到被量測的那個數字</a>那張卡用的正是這個度量當實例。</p>
<p>CRAP 度量把它跟覆蓋率合起來：<code>CRAP(m) = C²(1 − cov)³ + C</code>，其中 <code>C</code> 是圈複雜度、<code>cov</code> 是該函式的覆蓋率。滿覆蓋率時 CRAP 等於圈複雜度本身，所以「CRAP 低於 4」等價於「每個函式的複雜度不超過 3」。</p>
<h2 id="可觀察訊號與例子">可觀察訊號與例子</h2>
<p>一個 <code>if</code> 加一個 <code>for</code> 加一個三元運算子的函式，複雜度是 4，完全覆蓋它至少要四個案例。同一段邏輯改用查表取代分支，複雜度會掉到 1 而行為不變——這是它與「這段程式難不難懂」脫鉤的地方：查表版可能更難懂，指標卻好看得多。</p>
<p>門檻設在 10 上下是工具的常見預設，出處是原始論文的建議值而不是實證；設在 3 是特定實驗的參數，不是通用建議。看到一個複雜度門檻時要先問這個數字從哪來。</p>
<h2 id="設計責任">設計責任</h2>
<p>拿它當閘門的人要同時指定它量不到的那個維度該由誰檢查。可機械執行的是<strong>觸發</strong>——每個新產生的函式名都被問一次；判定那個名字對不對得回一個領域概念，仍然是人的工作，可以要求它在需求文件或領域詞彙表裡找得到對應。沒有配對這一步時，最便宜的達成方式（把違規處切碎）與真正的分解在指標上完全一樣。</p>
]]></content:encoded></item></channel></rss>