人類用計算機處理文字主要是依賴巨集語言以及一些專用的文字編輯器。事實上,早期的文字編輯器只提供基本的文字編輯功能,然後借助巨集語言進行功能擴充套件。結果人類很快就發現,基於巨集擴充套件的編輯器,功能越複雜,它的行為就越詭異。於是,文字編輯器的擴充套件語言很快就被換成了當時的一種通用的動態型別的函式式程式語言——lisp。實際上,這就是 emacs 的前世與今生。
從巨集處理器的角度來看,巨集語言中只有兩種型別:文字與巨集。巨集展開的結果是文字,但巨集本身也是文字,二者的界限往往不是那麼明顯。在 m4 中,往往要借助引號來區分巨集與普通文字,而引號本身又有可能是文字。型別如此貧弱,因此很容易在巨集定義時引入一些並不顯而易見的錯誤,而這些錯誤無法被其他程式檢測。另外,用巨集語言編寫的複雜程式一旦在執行時出現問題,就很難準確定位問題所在,因為錯誤是在巨集展開的結果中發現的,發現錯誤的時候,很難快速確定它是哪個巨集的展開結果。
雖然 dennis ritchie 是 m4 的設計者之一,但是他並沒有將 m4 作為 c 語言的巨集處理器,而是為 c 語言設計了一種更為輕巧、簡單的巨集處理機制,顯然這是有意而為之。
eric raymonad 在《unix 程式設計藝術》一書中指出,功能越強大的巨集處理器,越有可能帶來更糟糕的麻煩。tex 引擎就是一種功能非常強大的巨集處理器,但是要用它來做程式設計方面的事,也許定義兩個數的除法運算就需要上百行巨集**,這種級別**複雜度導致 tex 巨集比 perl 恐怖多了。有一些新的 tex 引擎正在引入某種通用的程式語言來替換 tex 的巨集擴充套件機制。例如 luatex,在乙個重構的 tex 引擎基礎上將 lua 作為擴充套件語言,也有嘗試將 scheme 作為 tex 擴充套件語言的。
雖然現在幾乎看不到巨集語言的應用了,但是它依然默默的在工作著。幾乎所有的 linux 系統都離不開 gnu autotools 工具集。這個工具集就是基於 m4 語言構建的,其開發者將一些特定功能的 shell **封裝到一些 m4 巨集中,然後由 gnu m4 負責將其展開為 shell **。例如,下面這份簡單的 m4 巨集**只有 9 行:
ac_init([m5], [0.1])但是它展開後的所得的 shell **卻長達 5000 余行。而我在寫這 9 行**的時候,我幾乎完全不懂 shell 語言,但是我卻能理解這些 m4 巨集的含義,因為它們只是軟體構建過程的一種抽象。ac_config_aux_dir([build-aux])
ac_config_macro_dir([m4])
am_init_automake([foreign -wall -werror subdir-objects])
ac_prog_cc
am_prog_cc_c_o
ac_config_headers([config.h])
ac_config_files([makefile])
ac_output
事實上,tex 原本也是這樣。donald knuth 所開發的 tex 系統,其排版原語只有 300 多個,但是通過 tex 巨集可以將這些排版原語組合起來,從而完成更為複雜的排版任務。對於這種任務,巨集語言的執行效率要高於一種通用的程式語言。對於 knuth 而言,這一決策是正確的,因為這樣的 tex 完全滿足了他的需求。後來隨著排版任務的複雜化,巨集的侷限性就日益的呈現了出來。如果始終堅持用巨集的方式來擴充套件 tex 的功能,進度是緩慢的,參與者的數量是逐步減少的,而且這一切都依賴於底層不能發生任何變化。這種系統遲早會變成恐龍的。knuth 的 tex 只支援 8 位字元,後來要讓它支援中文,hacker 們不得不絞盡腦汁的在巨集包的層面上去做工作,以至於如何讓 tex 支援中文,對於中文使用者而言,長期以來一直是初學者遇到的第乙個本來不應該是障礙的障礙。
濫用巨集語言所提供的程式設計能力,所產生的問題往往要比它解決的問題更多。許多現代的程式語言已經不再提供巨集機制,c++ 雖然支援 c 語言巨集,但是它幾乎不停的告誡程式設計師最好不要用巨集,而是用 const 或內聯函式。
可以用巨集去薄層封裝那些繁瑣且需要多次重複使用的**,但是原則上不要用巨集去實現過於複雜的邏輯。
我定義了乙個 m4 巨集 indent,它可以將乙個文字塊整體縮排一定距離。例如:
indent(`m4 的展開結果為:foo bar
bar foo
foo bar
這個巨集的定義如下:foo bar
bar foo
foo bar
define(這 10 行**,讓我寫了差不多半個下午,大部分時間都在與引號戰鬥。m4 巨集如果出錯,首先應該是排查引號的錯誤。當我好不容易讓這幾行**能夠成功執行之後,我發現它脆弱不堪,很容易崩潰。例如:new_line',
define(`indent』,
`$2』)』)』,)』)`ifelse(eval(len(`$1') > 1),
1,`ifelse(substr(`$1',0,1),
new_line,
`format(`%s%s', new_line,`$2')`'indent(substr(`$1',1,eval(len(`$1')-1)), `$2')',
`substr(`$1',0,1)`'indent(substr(`$1',1,eval(len(`$1')-1)),
indent(a(b)',
')
m4 試圖對其進行展開,然後它就會抱怨:
error: end of file in argument list
因為在 indent 的遞迴展開過程中,a(b) 中的 ( 或 ) 均會被 m4 錯認為是某個巨集的引數列表的括號。由於 m4 不提供逃逸符,所以只能在 indent 的遞迴過程中去檢測像 (,) 以及 ,這樣的符號,然後特殊處理。如果在上面這 10 行**的基礎上再增加處理這些特殊情況的**……結果就是,說一句謊言,要用一百句謊言來掩蓋。即使 m4 提供逃逸符,也不怎麼會有人打算在**中為頻繁出現的 ( 與 `)』 之類的符號新增逃逸符的。
幸好,gnu m4 提供了 patsubst 巨集,使用它可實現 indent 希望實現的功能:
patsubst(`gnu m4 的展開結果為:abca(b)
c(a), e, f g』, `
')
由此可見,如果乙個採用了巨集擴充套件策略的系統,它所提供的『原語』級的實現有多麼的重要!abc
a(b)
c(a), e, f g
為何受歡迎
自從現代工業誕生以來,圍繞在產品周圍的便是人們設計產品時所追尋的兩種路線 一種是追求各種高精尖技術都應用到同乙個產品中,使得自己的產品在領域內成為科技含量遙遙領先的一枝獨秀 另一種便是以客戶為核心,一切為了方便客戶 滿意客戶而設計產品的功能和外形。兩種境界各有優缺點。首先高技術意味著自己的產品和別人...
Instagram為何如此受歡迎?
理性分析了一番,總結起來,它匯集了專注 藝術氣息 大眾點評,分享數量限制為一體。或許你會困惑,為什麼分享數量的限制也成為其成功的一部分,下面就告訴你原因。質量不是太高,但卻能讓人感受到藝術氣息以及介面的簡約與精緻 不管你是否喜歡手機拍照和濾鏡效果,不可否認的是,喜歡的它人越來越多了,和hipstam...
職場不受歡迎的八種人
知識陳舊的人 如今,知識更新的速度越來越快,知識倍增的週期越來越短。生活在這樣乙個時代,任何人都必須不斷學習,更新知識,想靠學校學的知識 應付 一輩子,已完全不可能了。專家說,過去,人們對終身教育的理解是,乙個人從上學到退休,要一直接受教育 現在,這一概念應當重新定義,終身教育,從搖籃到墳墓,應貫穿...