dc (程序)

dc(desk calculator:桌面計算器)是採用逆波蘭表示法跨平台計算器,它支持任意精度算術[1]。它是Robert Morris英語Robert Morris (cryptographer)貝爾實驗室期間書寫的[2],作為最老的Unix實用工具,先於C語言的發明。像那個年代的其他實用工具一樣,它有着一組強力的特徵和簡潔的語法[3][4]。傳統上,採用中綴表示法bc計算器程序是在dc之上實現的。

dc
原作者Robert Morris英語Robert Morris (cryptographer)
(於AT&T貝爾實驗室
Lorinda Cherry英語Lorinda Cherry
開發者各種開源商業開發者
編程語言B
操作系統Unix, 類Unix, Plan 9
平台跨平台
類型命令
許可協議 編輯維基數據鏈接

歷史

dc是倖存的最老的Unix語言[2]。在貝爾實驗室收到第一台PDP-11的時候,用B語言寫成的dc是在這個新機器上運行的第一個語言,甚至在匯編器之前[5]

基本運算

在dc中要做4和5的乘法:

$ dc
4 5 *
p
20
q

這可轉譯為「把4和5壓入棧頂,通過乘法算符,從棧中彈出兩個元素,將二者相乘並把結果壓回棧頂」。接着使用p命令打印棧頂的元素。使用q命令退出此次調用的dc實例。注意數值相互間必須以空白分隔,但某些算符可以不必如此。 還可以用如下命令得到這個結果:

$ dc -e '4 5 * p'
20
$ echo "4 5 * p" |dc
20
$ dc -
4 5*pq
20
$ cat <<EOF > cal.txt
4 5 *
p 
EOF
$ dc cal.txt
20

使用命令k來變更算術精度,它設置算術運算的小數位數。因為缺省精度是0,例如:

$ dc -e "2 3 / p"
0

通過使用命令k調整精度,可以產生任意數目的小數數位,例如:

$ dc -e "5 k 2 3 / p"
.66666

dc有科學計算器的基本運算功能,比如求 的值:

$ dc -e "2k 12 _3 4 ^ + 11 / v 22 - p"
-19.10

其中,_用於輸入負數,^計算冪,v計算平方根。

使用d命令複製棧頂元素。使用r命令對棧頂和僅次棧頂的兩個元素進行對換。使用z命令壓入當前棧深度,即執行z命令前棧中元素的數目。

輸入/輸出

使用?命令,從stdin讀取一行並執行它。這允許從宏中向用戶要求輸入,故而此輸入必須是語法上正確的,並且這有潛在的安全問題,因為dc的!命令可以執行任意系統命令。

前面提及過,p命令打印棧頂元素,帶有隨後的一個換行。n命令彈出棧頂元素並輸出它,沒有尾隨換行。f命令打印整個棧,一項一行。

dc還支持控制輸入和輸出的基數i命令彈出棧頂元素並將它用作輸入基數。十六進制數字必須大寫以避免和dc命令衝突,輸入基數必須在2和16之間,輸出基數必須大於等於2。o命令設置輸出基數,要記住輸入基數將影響對後面的所有數值的分析,所以通常建議先設置輸出基數。例如將二進制轉換成十六進制:

$ echo 16o2i 11011110101011011011111011101111p | dc
DEADBEEF

要讀取設置的這些數值,KIO命令將壓入當前精度、輸入基數和輸出基數到棧頂。

語言特徵

除了上述的基本算術和棧操作,dc包括了對、條件和存儲結果用於以後檢索的支持。

寄存器

寄存器在dc中是有着單一字符名字的存貯位置,它可以通過命令來存儲和檢索,它是宏和條件的底層機制:sc彈出棧頂元素並將它存儲入寄存器c,而lc將寄存器c的值壓入棧頂。例如:

 3 sc 4 lc * p

寄存器還被當作次要棧,可以使用SL命令在它們和主要棧之間壓入和彈出數值。存儲棧頂元素到寄存器中並把這個元素留在棧頂,需要聯合使用ds命令。

字符串

字符串是包圍在[]之中的字符,可以被壓入棧頂和存入寄存器。使用x命令從棧頂彈出字符串並執行它,使用P命令從棧頂彈出並打印字符串,無尾隨換行。a命令可以把數值的低位字節轉換成ASCII字符,或者在棧頂是字符串時把它替換為這個字符串的第一個字符。此外沒有方法去建造字符串或進行字符串操縱。

#字符開始一個注釋直到此行結束。

通過允許寄存器和棧項目像數值一樣存儲字符串,從而實現了。一個字符串可以被打印,也可以被執行,就是說作為dc命令的序列而傳遞。例如可以把一個宏「加1並接着乘以2」存儲到一個寄存器m中:

 [1 + 2 *] sm

通過使用x命令彈出棧頂的字符串並執行之,如下這樣使用存儲的宏:

 3 lm x p

Q命令從棧頂彈出一個值作為退出宏的層數,比如2Q命令退出2層宏,它永不導致退出dc。q命令退出2層宏,如果宏少於2層則退出dc。

條件

最後提供了有條件執行宏的機制。命令=r將從棧頂彈出兩個值,如果二者相等,則執行存儲在寄存器r中的宏。如下命令序列將在原棧頂元素等於5的條件下打印字符串equal

[[equal]p] sm d 5 =m

這裡使用了d命令保留原棧頂元素。其他條件有>!><!<!=,如果棧頂元素分別大於、不大於(小於等於)、小於、不小於(大於等於)、不等於僅次於棧頂的元素,則執行指定宏。

迭代

通過定義有條件的調用自身的遞歸宏,迭代也是可行的。一個簡單的對棧頂元素的階乘過程

 # F(x): return x!
 # if x-1 > 1
 #    return x * F(x-1)
 # otherwise
 #    return x

可實現為:

 [d1-d1<F*]dsFxp

這裡宏中的第一個d命令相當於分配了一個局部變量。

例子

Unix V7手冊頁舉出的編程實例為打印階乘n!的前10個值:

$ dc -e "[la1+dsa*pla10>y]sy 0sa1 lyx"
1
2
6
24
120
720
5040
40320
362880
3628800

這個程序實現了For循環,將作為循環體的宏[la1+dsa*pla10>y]存儲在寄存器y中;把寄存器a作為循環計數器,設其初始值為0,將0!的值1壓入棧頂;從寄存器y中取出宏並執行之。宏中的la1+dsa將計數器a的數值加1,並將這個值留在棧頂;隨後*p從棧中彈出兩個元素進行乘法並把結果壓入棧中,打印這個結果;隨後la10>y將計數器a的數值和數值10壓入棧中,判斷位於棧頂的10是否大於計數器的數值,即計數器的數值是否小於10,彈出二者並在判斷成立的條件下再次執行存儲在寄存器y中的宏。計數器a的數值從0增加到10,宏一共被執行了10次。

參見

引用

  1. ^ dc(1): an arbitrary precision calculator – Linux用戶命令(User Commands)手冊頁
  2. ^ 2.0 2.1 Brian Kernighan and Ken Thompson. A nerdy delight for any Vintage Computer Fest 2019 attendee: Kernighan interviewing Thompson about Unix. YouTube. 事件發生在 29m45s. [September 3, 2019]. (原始內容存檔於2022-02-01). 
  3. ^ The sources for the manual page for 7th Edition Unix dc. [2020-09-25]. (原始內容存檔於2019-09-24). 
  4. ^ Ritchie, Dennis M. The Evolution of the Unix Timesharing System. Sep 1979 [2019-05-31]. (原始內容存檔於2010-05-06). 
  5. ^ McIlroy, M. D. A Research Unix reader: annotated excerpts from the Programmer's Manual, 1971–1986 (PDF) (技術報告). CSTR. Bell Labs. 1987 [2019-05-31]. 139. (原始內容存檔 (PDF)於2019-11-30). 

外部連結