命令行计算器概览
当需要做点数值计算时,当然可以用很多种编程语言。但有时我们只想方便一点,于是unix下有一些小计算器。
expr
expr的用法很简单:
expr 表达式
另外,shell命令中也可用$((表达式)),它会被替换为结果。
expr支持下列运算,按优先级递增为:
| 运算 | 值 |
|---|---|
| ARG1 &124; ARG2 | ARG1非零非null时ARG1,否则ARG2 |
| ARG1 & ARG2 | 两个参数都非零非null时ARG1,否则0 |
| ARG1 < ARG2 | 小于 |
| ARG1 <= ARG2 | 小于等于 |
| ARG1 = ARG2 | 等于 |
| ARG1 != ARG2 | 不等 |
| ARG1 >= ARG2 | 大于等于 |
| ARG1 > ARG2 | 大于 |
| ARG1 + ARG2 | 加法 |
| ARG1 - ARG2 | 减法 |
| ARG1 * ARG2 | 乘法 |
| ARG1 / ARG2 | 除法 |
| ARG1 % ARG2 | 求余 |
| STRING : REGEXP | STRING是否匹配REGEXP,有(时返回(与)间匹配部分或null,否则返回匹配的字符数 |
| match STRING REGEXP | 相当于 STRING : REGEXP |
| substr STRING POS LENGTH | STRING由POS(由1开始)的子字符串 |
| length STRING | STRING的长度 |
| + TOKEN | 把TOKEN视为字符串,即使它是运算符 |
| ( EXPRESSION ) | EXPRESSION的值 |
其中仅当参数都为数值时比较为数值比较,否则为字符串比较。
expr在结果非零非null时返回0,否则返回1。语法错误返回2,其它错误返回3。
dc
dc是桌面计算器(desk calculator)的缩写,它处理后缀表达式(也称逆波兰式),可以完成任意精度的定点(而非浮点)运算,甚至使用宏。
后缀表达式的优点在于计算机容易解读和处理,还省去优先级问题。可惜大家习惯了糟糕的中缀表达式,所以反而觉得dc奇特。
dc显然用栈架构实现,打开dc后每输入一个数值(整数或小数,但不支持指数),就被压入栈中,两个相邻数值用空格或换行分隔。每输入一个被方括号包围的字符串也会被压入栈中。当碰到以下的命令,会作相应操作。
| 打印命令 | 用途 |
|---|---|
| p | 打印栈顶再换行 |
| n | 打印并弹出栈顶 |
| P | 弹出栈顶,字符串的话打印之,否则绝对值的整数部分以字节序列形式打印 |
| f | 打印栈 |
| 算术命令 | 用途 |
|---|---|
| + | 弹出两个元素并推入它们之和 |
| - | 弹出两个元素并推入它们之差 |
| * | 弹出两个元素并推入它们之积 |
| / | 弹出两个元素并推入它们之商(先弹出的为除数) |
| % | 弹出两个元素并推入它们之余(先弹出的为除数) |
| ~ | 弹出两个元素并推入它们之商和余(先弹出的为除数) |
| ^ | 弹出两个元素并推入它们之幂(先弹出的为被指数) |
| | | 幂模(先弹出模、再弹出指数、然后底) |
| v | 弹出栈顶并推入其平方根 |
| 栈控制命令 | 用途 |
|---|---|
| c | 清空栈 |
| d | 重复压入栈顶 |
| r | 交换栈顶和栈顶下面的一个元素 |
dc提供至少256个内存寄存器,分别以单个字符命名,每个寄存器都是栈。
| 寄存器命令 | 用途 |
|---|---|
| sr | 弹出栈顶并把它压入寄存器r |
| lr | 把寄存器r的栈顶推入主栈 |
| Sr | 弹出栈顶并把它压入寄存器r |
| Lr | 弹出寄存器r的栈顶推入主栈 |
进制可以从2到16。精度为非负整数,表示保留的小数位个数,默认0即除加减法外结果为整数。
| 参数命令 | 用途 |
|---|---|
| i | 弹出栈顶用作输入进制 |
| o | 弹出栈顶用作输出进制 |
| k | 弹出栈顶用作精度 |
| I | 把当前输入进制推入栈 |
| O | 把当前输出进制推入栈 |
| K | 把当前精度推入栈 |
| 字符串命令 | 用途 |
|---|---|
| a | 弹出栈顶,若为数值则把其最低字节转换为字符串推入栈,否则推入首个字符 |
| x | 弹出栈顶并作为宏执行之 |
| >r | 若先弹出的大于接着弹出的,则执行寄存器r的内容 |
| !>r | 若先弹出的不大于接着弹出的,则执行寄存器r的内容 |
| <r | 若先弹出的小于接着弹出的,则执行寄存器r的内容 |
| !<r | 若先弹出的不小于接着弹出的,则执行寄存器r的内容 |
| =r | 若先弹出的等于接着弹出的,则执行寄存器r的内容 |
| !=r | 若先弹出的不等于接着弹出的,则执行寄存器r的内容 |
| ? | 从终端读一行并执行之 |
| q | 退出宏和,调用它的宏。如果没有宏在执行,退出dc |
| Q | 弹出栈顶并退出那么多层宏执行 |
| 状态查询命令 | 用途 |
|---|---|
| Z | 弹出栈顶并推入其不含前导零的位个数(字符串为字符数) |
| X | 弹出栈顶并推入其小数位个数(字符串为0) |
| z | 推入当前栈深 |
| 杂项命令 | 用途 |
|---|---|
| ! | 把本行余下部分看作系统命令,以 <、 =或>开始的命令名前加空格 |
| # | 把本行余下部分看作注释 |
| :r | 把数组r的下标为先弹出者的元素设为接着弹出的 |
| ;r | 弹出栈顶并把数组r以它为下标的元素压入栈 |
和unix其它小语言一样,紧凑有时导致难读,dc有一个非常著名的例子,是一个用Perl实现的RSA公共密钥算法,广泛发布在签名档和T恤上,以示对美国1995年的限制密码学出口的抗议。
print pack"C*",split/D+/,`echo"16iII*oU@{$/=$z;[(pop,pop,unpack "H*",<>)]}EsMsKsN0[1N*11K[d2%Sa2/d0<x+d*lmLa^*1N%0]dsXx++1M1N/dsM0<J]dsJxp"|dc`
bc
bc则处理中缀表达式,可以完成任意精度的定点(而非浮点)运算,并有d类似C语言的控制结构。但很多方面都可看出其设计不完善。
bc可用/和/包围注释,或者用#开始行末注释。bc用换行或分号分隔语句。不完整语句会导致自动续行,也可用反斜杠续行。
常量和变量
bc中数值常量可有小数点,数值可以有任意精度,内部用十进制表示。
bc中字符串常量用被双引号包围的字符串表示,可用类似C的转义序列。
bc中变量名从小写字母开始,后接若干小写字母、数字或下划线。变量同时可作一维数组变量(保存一些以数值(可以不是整数)为下标索引的数值,用方括号指定下标)和简单变量(保存一个数)。未赋值的变量的值为0。
| 特殊变量 | 用途 |
|---|---|
| scale | 运算中保留的小数位数 |
| ibase | 输入进制,默认10 |
| obase | 输出进制,默认10 |
| last | 保存上次打印的数 |
表达式
和C语言类似,表达式由常量的变量经运算符、括号和函数调用(形如函数名(参数列表),其中数组变量后缀[])组合而成。
运算符优先级从高到低为:
- 不结合:++ var、– var、var ++、var –
- 不结合:- expr
- 右结合:expr ^ expr
- 左结合:expr * expr、 expr / expr、 expr % expr
- 左结合:expr + expr、 expr - expr
- 右结合:var = expr、 var
= expr - 左结合:expr1 < expr2、 expr1 <= expr2、 expr1 > expr2、 expr1 >= expr2、 expr1 == expr2、 expr1 != expr2
- 不结合:!expr
- 左结合:expr && expr
-
左结合:expr expr
注意这顺序与C语言不同,如a = 3 < 5会把变量a设为3而非1。另外,数值运算以十进制进行。
标准的函数有:
| 表达式 | 值 |
|---|---|
| length(表达式) | 有效位数(小数点后的都视为有效),如length(.000001)为6,length(1935.000)=7 |
| read() | 从标准输入读的一个数(小心与代码混淆),如length(.000001)为6,length(1935.000)=3 |
| scale(表达式) | 小数点后位数 |
| sqrt(表达式) | 平方根 |
启用-l选项的话还可用以下数学函数:
| 表达式 | 值 |
|---|---|
| s(x) | x的正弦 |
| c(x) | x的余弦 |
| a(x) | x的反正切 |
| l(x) | x的自然对数 |
| e(x) | $e^x$ |
| j(n,x) | x的n阶Bessel函数值 |
语句
| 语句 | 用途 | |
|---|---|---|
| 表达式 | 求值表达式,非赋值表达式会打印结果 | |
| 常量 | 打印它 | |
| print 逗号分隔的表达式或字符串列表 | 打印各值 | |
| { 语句序列 } | 复合语句 | |
| if ( expression ) statement1 [else statement2] | 与C类似 | |
| while ( expression ) statement | 与C类似 | |
| for ( [expression1] ; [expression2] ; [expression3] ) statement | 与C类似 | |
| break | 与C类似 | |
| continue | 与C类似 | |
| halt | 退出bc | |
| return | 返回0 | |
| return 表达式 | 与C类似 |
另外,在编译时碰到quit语句会退出bc,碰到limits会输出实现限制。
函数定义形如
define 名称 ( 参数列表 ) {
局部变量列表
一系列语句
}
其中,参数列表中零个或多个名字由逗号分隔(数组要加后缀[]),参数一般按值传递(除非数组参数加前缀*)。
可选的局部变量列表形如auto 名字,...;,分号也是可选的。由于bc用简单的栈结构实现局部变量,当函数a调用b时,b可以访问a的局部变量。
函数体中常量按调用时的ibase解读。不指定返回值将返回0。