dcdesk 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 "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

要读取设置的这些数值,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 lmx p

Q命令从栈顶弹出一个值作为退出宏的层数,比如2Q命令退出2层宏,它永不导致退出dc。q命令退出2层宏,如果宏少于2层则退出dc。

条件

编辑

最后提供了有条件执行宏的机制。命令=r将从栈顶弹出两个值,如果二者相等,则执行存储在寄存器r中的宏。如下命令序列将在原栈顶元素等于5的条件下打印字符串equal

[[equal]p]sm d5=m

这里使用了d命令保留原栈顶元素。其他条件有>!><!<!=,如果栈顶元素分别大于、不大于(小于等于)、小于、不小于(大于等于)、不等于仅次于栈顶的元素,则执行指定的宏。注意不同于ForthPostScriptFactor,在不等式比较中的运算元的次序同在算术中的次序相反,5 3 - 等价于中缀表示法的5 - 3,然而5 3 <t3 < 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)

这里的伪代码采用了条件运算符而非条件语句。它可实现为:

[d 1- lFx *]sG [d1<G]dsFx

不能直接实现x > 1 ? G(x) : 1,但可以增加针对输入值0的预处理。下面是其执行示例:

$ echo 0 | dc -e '? [sb1]sad0!<a [d 1- lFx *]sG [d1<G]dsFx p'
1
$ echo 9 | dc -e '? [sb1]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

参见

编辑

引用

编辑
  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). 

外部链接

编辑