最优雅的unixshell是一行程序,可以对任意精度的数字列表求和吗?
关于整数加一行程序,存在几种建议的shell脚本解决方案最优雅的unixshell是一行程序,可以对任意精度的数字列表求和吗?,shell,awk,scripting,bc,dc,Shell,Awk,Scripting,Bc,Dc,关于整数加一行程序,存在几种建议的shell脚本解决方案 然而,仔细观察所选择的每种解决方案,都有其固有的局限性: awkone将以任意精度和整数大小阻塞(毕竟它的行为类似于C) bc人们宁愿对任意长的输入不满意:(sed's/$/+\\\/g';echo 0)| bc 了解到除了跨平台的可移植性(参见[1][2])之外,还可能存在一些不希望出现的问题, 是否有一种通用解决方案在实用性和简洁性方面都是赢家 提示:SunOS和MacOSX是可移植性问题的例子。 菲。dc命令是否允许处理任意大的
然而,仔细观察所选择的每种解决方案,都有其固有的局限性:
one将以任意精度和整数大小阻塞(毕竟它的行为类似于C)awk
人们宁愿对任意长的输入不满意:bc
(sed's/$/+\\\/g';echo 0)| bc
菲。
dc
命令是否允许处理任意大的2^n整数或其他输入
[1] awk
:或
[2]
bc
:看来下面的方法可以奏效:
$ seq 1000|dc -f - -e '[+z1<r]srz1<rp'
500500
$seq 1000 | dc-f--e'[+z1似乎下面的方法可以奏效:
$ seq 1000|dc -f - -e '[+z1<r]srz1<rp'
500500
$seq 1000 | dc-f--e'[+z1我通常使用的是粘贴-sd+| bc
:
$ time seq 1 20000000 | paste -sd+|bc
200000010000000
real 0m10.092s
user 0m10.854s
sys 0m0.481s
(为了严格遵守Posix标准,paste
需要提供一个明确的参数:paste-sd+-|bc
。显然,这对于默认安装在OS X上的BSDpaste
实现是必要的。)
但是,对于较大的输入,这将失败,因为bc
在计算之前会在内存中缓冲整个表达式。在我的系统上,bc
在尝试添加1亿个数字时内存不足,尽管它可以添加7000万个数字。但其他系统的容量可能较小
由于bc
有变量,您可以通过重复添加到变量而不是构造单个长表达式来避免长行。这(据我所知)是100%符合Posix的,但有3倍的时间代价:
$ time seq 1 20000000|sed -e's/^/s+=/;$a\' -es|bc
200000010000000
real 0m29.224s
user 0m44.119s
sys 0m0.820s
处理输入大小超过bc
缓冲容量的情况的另一种方法是使用标准xargs
工具分组添加数字:
$ time seq 1 100000000 |
> IFS=+ xargs sh -c 'echo "$*"' _ | bc | paste -sd+ | bc
5000000050000000
real 1m0.289s
user 1m31.297s
sys 0m19.233s
每个xargs
求值所使用的输入行数因系统而异,但通常会有数百行,而且可能更多。显然,xargs | bc
调用可以任意链接以增加容量
在ARG_MAX
超过bc
命令容量的系统上,可能需要使用-s
开关限制xargs
扩展的大小。除了执行实验以确定bc
缓冲区限制外,没有可移植的方法来确定该限制可能是什么但它肯定不应小于保证至少为2048的LINE_MAX
。即使使用100位加法器,也将允许减少20倍,因此,假设您准备等待几个月才能完成,由10个xargs | bc
管道组成的链将处理1013个以上的加法器
作为构建大型固定长度管道的替代方法,您可以使用函数递归地从xargs | bc
输出管道,直到只生成一个值:
radd () {
if read a && read b; then
{ printf '%s\n%s\n' "$a" "$b"; cat; } |
IFS=+ xargs -s $MAXLINE sh -c 'echo "$*"' _ |
bc | radd
else
echo "$a"
fi
}
如果对MAXLINE
使用一个非常保守的值,则上述速度相当慢,但如果值较大,则速度不会比简单的paste | bc
解决方案慢多少:
$ time seq 1 20000000 | MAXLINE=2048 radd
200000010000000
real 1m38.850s
user 0m46.465s
sys 1m34.503s
$ time seq 1 20000000 | MAXLINE=60000 radd
200000010000000
real 0m12.097s
user 0m17.452s
sys 0m5.090s
$ time seq 1 100000000 | MAXLINE=60000 radd
5000000050000000
real 1m3.972s
user 1m31.394s
sys 0m27.946s
除了bc
解决方案外,我还对其他一些可能性进行了计时。如上所示,输入2000万个数字后,paste | bc
需要10秒。这几乎与使用
gawk -M '{s+=$0} END{print s}'
诸如python
和perl
之类的编程语言被证明速度更快:
# 9.2 seconds to sum 20,000,000 integers
python -c $'import sys\nprint(sum(int(x) for x in sys.stdin))'
# 5.1 seconds
perl -Mbignum -lne '$s+=$_; END{print $s}'
我无法测试dc-f--e'[+z1我通常使用的是paste-sd+| bc
:
$ time seq 1 20000000 | paste -sd+|bc
200000010000000
real 0m10.092s
user 0m10.854s
sys 0m0.481s
(为了严格遵守Posix标准,paste
需要提供一个明确的参数:paste-sd+-|bc
。显然,这对于默认安装在OS X上的BSDpaste
实现是必要的。)
但是,对于较大的输入,这将失败,因为bc
在计算之前会在内存中缓冲整个表达式。在我的系统上,bc
在尝试添加1亿个数字时内存不足,尽管它可以添加7000万个数字。但其他系统的容量可能较小
由于bc
有变量,您可以通过重复添加到变量而不是构造单个长表达式来避免长行。这(据我所知)是100%符合Posix的,但有3倍的时间代价:
$ time seq 1 20000000|sed -e's/^/s+=/;$a\' -es|bc
200000010000000
real 0m29.224s
user 0m44.119s
sys 0m0.820s
处理输入大小超过bc
缓冲容量的情况的另一种方法是使用标准xargs
工具分组添加数字:
$ time seq 1 100000000 |
> IFS=+ xargs sh -c 'echo "$*"' _ | bc | paste -sd+ | bc
5000000050000000
real 1m0.289s
user 1m31.297s
sys 0m19.233s
每个xargs
求值所使用的输入行数因系统而异,但通常会有数百行,而且可能更多。显然,xargs | bc
调用可以任意链接以增加容量
在ARG_MAX
超过bc
命令容量的系统上,可能需要使用-s
开关限制xargs
扩展的大小。除了执行实验以确定bc
缓冲区限制外,没有可移植的方法来确定该限制可能是什么但它肯定不应小于保证至少为2048的LINE_MAX
。即使使用100位加法器,也将允许减少20倍,因此,假设您准备等待几个月才能完成,由10个xargs | bc
管道组成的链将处理1013个以上的加法器
作为构建lar的替代方案