最优雅的unixshell是一行程序,可以对任意精度的数字列表求和吗?

最优雅的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命令是否允许处理任意大的

关于整数加一行程序,存在几种建议的shell脚本解决方案
然而,仔细观察所选择的每种解决方案,都有其固有的局限性:

  • awk
    one将以任意精度和整数大小阻塞(毕竟它的行为类似于C)
  • bc
    人们宁愿对任意长的输入不满意:
    (sed's/$/+\\\/g';echo 0)| bc
了解到除了跨平台的可移植性(参见[1][2])之外,还可能存在一些不希望出现的问题, 是否有一种通用解决方案在实用性和简洁性方面都是赢家

提示:SunOS和MacOSX是可移植性问题的例子。
菲。
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上的BSD
paste
实现是必要的。)

但是,对于较大的输入,这将失败,因为
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上的BSD
paste
实现是必要的。)

但是,对于较大的输入,这将失败,因为
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的替代方案