※ 引述《lambking (BB)》之銘言:
: 想要寫一個function 去計算一個字串中某元素出現的次數
: 例如:
: list={a,a,b,b,c,c,c,} ;
: frequncycount[list]
: output:
: {{a,2},{b,2},{c,3}}
: 請問有什麼方法能夠寫這個function ?
: 謝謝
誠如chungyuandye所言
Mathematica內建的Tally指令是這個問題最好最快的解法
Tally甚至還能自訂"相同"元素的比對函數
但如果題目稍微變化一下呢?
像是想以連續相同元素個數對表示一串列
例如將{a,a,a,b,b,a,b,b,b,a,a}變成{{a,3},{b,2},{a,1},{b,3},{a,2}}
該怎麼計算呢?
當然可以用迴圈輕易地達成
不過這就無法發揮Mathematica的長處了
其實這類問題能夠非常直覺地以pattern跟rule解決
這也是Mathematica另一個強大之處
直接寫出原po問題的pattern/rule解法:
{#, 1} & /@ list //. {head___, {x_, n_}, mid___, {x_, m_}, tail___} ->
{head, mid, tail, {x, n + m}}
這麼直白的程式碼 相信許多人已經了解了其計算原理
一開始的 {#,1} & /@ list 利用純函數轉換原串列
例如{a,b,a,b,c,c,c}將變為{{a,1},{b,1},{a,1},{b,1},{c,1},{c,1},{c,1}}
這邊的 1 就是代表出現次數
接著後面的 //. 表示將持續替換後面的rule直到結果不再變化為止
如果寫的是 /. 那就表示僅替換一次
這裡先點到功能,稍後會說明為什麼要使用 //.
重點來了
後面的rule是這個程式碼的計算核心:
{head___, {x_, n_}, mid___, {x_, m_}, tail___} -> {head, mid, tail, {x, n+m}}
簡要介紹一下:
head___、mid___、tail___中的三連續底線
表示這些pattern可以匹配任意序列(sequence),並且可為空序列
如果只有2個連續底線也可匹配任意序列,但不包含空序列
至於單一底線,則是匹配任意表示式(expression)
例如其後的x_、n_、m_等
初學者在這邊只要簡單地想像為單一元素即可
仔細看的話會發現箭頭的左邊出現了兩個 x_
這表示兩個pattern必須匹配相同的元素
而右邊只有出現一次
表示這個rule每作用一次就會減少一項
換句話說,它會合併相同的 x_ 項
並且將其出現次數相加
至於使用head___、mid___、tail___這三個pattern的原因
是為了表示這兩個x_之前、之後、之間可以存在任意序列,包含空序列
只要不斷地重複這個步驟 最後就能得到我們要的結果
這就是要使用 //. 的原因
介紹完這個解法之後,相信各位會覺得這真是太直接了
甚至可以說這個rule同時描述了問題以及解法
這是Mathematica pattern/rule好用之處
可惜的是,方便跟效率經常位於天平的兩端
這個方法的效率並無法與Tally相比
不過在串列不大的情況下,還是挺好用的
行文至此,忍不住要為各位出一道作業
如果將{x,n+m}換個位置:
{head___, {x_, n_}, mid___, {x_, m_}, tail___} -> {head,{x,n+m}, mid, tail}
輸出結果是相同的(但順序可能不同)
但效率變得奇差無比
各位可以思考一下原因
回到先前拋出來的問題:
如何以連續相同元素個數對表示一串列
解法非常直覺:
{#, 1} & /@ list //.{head___,{x_,n_},{x_,m_},tail___}-> {head, {x, n+m}, tail}
後話
pattern/rule不常被注意 但其實很好用
至於中文參考書籍可參考最近出版的Mathematica Cookbook