dc (程序)
dc(desk calculator:桌面計算機)是採用逆波蘭表示法的跨平台計算機,它支援任意精度算術[1]。它是Robert Morris在貝爾實驗室期間書寫的[2],作為最老的Unix實用工具,先於C語言的發明。像那個年代的其他實用工具一樣,它有著一組強力的特徵和簡潔的語法[3][4]。傳統上,採用中綴表示法的bc計算機程式是在dc之上實現的。
原作者 | Robert Morris (於AT&T貝爾實驗室) 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 "5k 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
要讀取設定的這些數值,K
、I
和O
命令將壓入當前精度、輸入基數和輸出基數到棧頂。
語言特徵
編輯除了上述的基本算術和棧操作,dc包括了對宏、條件和儲存結果用於以後檢索的支援。
暫存器
編輯暫存器在dc中是有著單一字元名字的存貯位置,它可以通過命令來儲存和檢索,它是宏和條件的底層機制:sc
彈出棧頂元素並將它儲存入暫存器c
,而lc
將暫存器c
的值壓入棧頂。例如:
3 sc 4 lc * p
暫存器還被當作次要棧,可以使用S
和L
命令在它們和主要棧之間壓入和彈出數值。儲存棧頂元素到暫存器中並把這個元素留在棧頂,需要聯合使用ds
命令。
字串
編輯字串是包圍在[
和]
之中的字元,可以被壓入棧頂和存入暫存器。使用x
命令從棧頂彈出字串並執行它,使用P
命令從棧頂彈出並列印字串,無尾隨換行。a
命令可以把數值的低位位元組轉換成ASCII字元,或者在棧頂是字串時把它替換為這個字串的第一個字元。此外沒有方法去建造字串或進行字串操縱。
#
字元開始一個注釋直到此行結束。
宏
編輯通過允許暫存器和棧專案像數值一樣儲存字串,從而實現了宏。一個字串可以被列印,也可以被執行,就是說作為dc命令的序列而傳遞。例如可以把一個宏「加1
並接著乘以2
」儲存到一個暫存器m
中:
[1 + 2 *]sm
通過使用x
命令彈出棧頂的字串並執行之,如下這樣使用儲存的宏:
3 lmx p
Q
命令從棧頂彈出一個值作為退出宏的層數,比如2Q
命令退出2層宏,它永不導致退出dc。q
命令退出2層宏,如果宏少於2層則退出dc。
條件
編輯最後提供了有條件執行宏的機制。命令=r
將從棧頂彈出兩個值,如果二者相等,則執行儲存在暫存器r
中的宏。如下命令序列將在原棧頂元素等於5的條件下列印字串equal
。
[[equal]p]sm d5=m
這裡使用了d
命令保留原棧頂元素。其他條件有>
、!>
、<
、!<
、!=
,如果棧頂元素分別大於、不大於(小於等於)、小於、不小於(大於等於)、不等於僅次於棧頂的元素,則執行指定的宏。注意不同於Forth、PostScript和Factor,在不等式比較中的運算元的次序同在算術中的次序相反,5 3 -
等價於中綴表示法的5 - 3
,然而5 3 <t
在3 < 5
時執行暫存器t
的內容。下面是其執行範例:
$ echo 5 | dc -e '? [[equal]p]sm d5=m'
equal
迭代範例
編輯階乘
編輯# F(x):
# x > 1 ? G(x) : x
# G(x):
# x * F(x-1)
它在x > 0
時有效,這裡的虛擬碼採用了條件運算子而非條件語句。它可實現為:
[d 1- lFx *]sG [d1<G]dsFx
常用的虛擬碼x > 1 ? G(x) : 1
不能在dc中直接實現,但可以增加針對x ≤ 0
情況的預處理。下面是其執行範例:
$ echo 0 | dc -e '? [d-1+]sad0!<a [d 1- lFx *]sG [d1<G]dsFx p'
1
$ echo 9 | dc -e '? [d-1+]sad0!<a [d 1- lFx *]sG [d1<G]dsFx p'
362880
# n := x
# i := 0
# F(x):
# i := i + 1
# x := x * i
# print(x)
# i < n ? F(x) : x
# F(1)
這裡迭代的棧頂值x
是 ,其初始值1
是 ;i
是非負整數,其遞增形成的整數集區間是[0, n]
。它可實現為:
sn 0si 1 [li1+dsi *p liln>F]dsFx
下面是其執行範例:
$ echo 6 | dc -e '? sn 0si 1 [li1+dsi *p liln>F]dsFx'
1
2
6
24
120
720
可以將它改為計算單個的階乘:
$ echo 0 | dc -e '? sn 0si 1 [li1+dsi * liln>F]dsFx p'
1
$ echo 9 | dc -e '? sn 0si 1 [li1+dsi * liln>F]dsFx p'
362880
斐波那契數
編輯# F(x):
# x > 1 ? G(x) : x
# G(x):
# F(x-1) + F(x-2)
可實現為:
[1-d 1- lFx r lFx +]sG [d1<G]dsFx
這裡的r
命令反轉(交換)棧頂兩個元素的次序。下面是其執行範例:
$ echo 0 | dc -e '? [1-d 1- lFx r lFx +]sG [d1<G]dsFx p'
0
$ echo 9 | dc -e '? [1-d 1- lFx r lFx +]sG [d1<G]dsFx p'
34
下面的例子列印出斐波那契數列的不含第 項的前 項:
# i := x
# a := 1
# F(x):
# i > 0 ? G(x) : x
# G(x):
# tmp := a
# a := x
# x := x + tmp
# print(x)
# i := i - 1
# F(x)
# F(0)
這裡迭代的棧頂值x
是 ,其初始值0
是在 時的 ;a
在 時是 ,其初始值1
是在 時的 ;i
是進行迭代的計數器。它可實現為:
si 1sa 0 [lardsa +p li1-si lFx]sG [li0<G]dsFx
下面是其執行範例:
$ echo 6 | dc -e '? si 1sa 0 [lardsa +p li1-si lFx]sG [li0<G]dsFx'
1
1
2
3
5
8
可以將它改為計算單個的斐波那契數:
$ echo 0 | dc -e '? si 1sa 0 [lardsa + li1-si lFx]sG [li0<G]dsFx p'
0
$ echo 9 | dc -e '? si 1sa 0 [lardsa + li1-si lFx]sG [li0<G]dsFx p'
34
參見
編輯參照
編輯- ^ Linux使用者命令(User Commands)手冊頁 : an arbitrary precision calculator –
- ^ 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).
- ^ The sources for the manual page for 7th Edition Unix dc. [2020-09-25]. (原始內容存檔於2019-09-24).
- ^ Ritchie, Dennis M. The Evolution of the Unix Timesharing System. Sep 1979 [2019-05-31]. (原始內容存檔於2010-05-06).
- ^ 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).