Ruby 对标准输入整数求和
Ruby sum stdin integers
我有:
$ ruby -v
ruby 2.0.0p648 (2015-12-16 revision 53162) [universal.x86_64-darwin16]
假设你有一个整数序列 1..n
一个 ruby 新手会像这样对序列求和:
$ ruby -e 's=0
for i in 1..500000
s+=i
end
puts s'
125000250000
现在假设我有来自 stdin
:
的相同序列
$ seq 1 500000 | ruby -lne 'BEGIN{s=0}
s+=$_.to_i
END{puts s} '
125000250000
到目前为止一切顺利。
现在将终值从 500,000 增加到 5,000,000:
$ ruby -e 's=0
for i in 1..5000000
s+=i
end
puts s'
12500002500000 <=== CORRECT
$ seq 1 5000000 | ruby -lne 'BEGIN{s=0}
s+=$_.to_i
END{puts s} '
500009500025 <=== WRONG!
它产生不同的总和。
awk
和 perl
都以相同的序列产生正确的结果:
$ seq 1 5000000 | awk '{s+=} END{print s}'
12500002500000
$ seq 1 5000000 | perl -nle '$s+=$_; END{print $s}'
12500002500000
为什么 ruby 产生的总和不正确?我不认为它是溢出,因为 awk
和 perl
在同一输入上正常工作。
结论:
感谢 David Aldridge 诊断此问题。
OS X 和 BSD seq
转换为 1,000,000 的浮点输出,而 GNU seq
支持任意精度整数。 OS X seq
作为大于 1,000,000 的整数来源是无用的。 OS X:
示例
$ seq 999999 1000002
999999
1e+06
1e+06
1e+06
ruby 方法 .to_i
静默地将部分字符串转换为整数,在本例中就是 'bug'。示例:
irb(main):002:0> '5e+06'.to_i
#=> 5
脚本中的 'correct' 行要么使用 $_.to_f.to_i
来使用浮点数,要么使用 Integer($_)
以使脚本不会静默失败。 awk
和 perl
将 5e+06 解析为浮点数,而 ruby
不隐式:
$ echo '5e+06' | awk '{print +0}'
5000000
$ echo '5e+06' | ruby -lne 'print $_.to_i+0'
5
感谢 Stefan Schüßler 就 .to_i
行为打开 Ruby feature request。
我不确定这是 100% 的答案,但我注意到:
seq 500000 500001 | ruby -lne 'BEGIN{}
puts $_
END{} '
500000
500001
...但是...
seq 5000000 5000001 | ruby -lne 'BEGIN{}
puts $_
END{} '
5e+06
5e+06
... 所以 #to_i 将值转换为整数所采用的 "relaxed" 方法仍然有效 ...
seq 5000000 5000001 | ruby -lne 'BEGIN{}
puts $_.to_i
END{} '
5
5
...但是更严格的#to_int不会
seq 5000000 5000001 | ruby -lne 'BEGIN{}
puts $_.to_int
END{} '
-e:2:in `<main>': undefined method `to_int' for "5e+06":String (NoMethodError)
编辑:我也注意到了:
seq 5000000 5000001
5e+06
5e+06
因此必须将 -f
标志传递给 seq 以获得整数格式。
再次编辑:
最终答案:
seq -f %f 1 5000000 | ruby -lne 'BEGIN{s=0}
s+=$_.to_i
END{puts s} '
12500002500000
为了解释电子符号输出,seq
的 OS X 手册页提供了一些见解:
Use a printf(3) style format to print each number. [...] The default is %g
.
因此,seq
的输出等同于Ruby的:
sprintf('%g', 100000)
#=> "100000"
sprintf('%g', 1000000)
#=> "1e+06"
我有:
$ ruby -v
ruby 2.0.0p648 (2015-12-16 revision 53162) [universal.x86_64-darwin16]
假设你有一个整数序列 1..n
一个 ruby 新手会像这样对序列求和:
$ ruby -e 's=0
for i in 1..500000
s+=i
end
puts s'
125000250000
现在假设我有来自 stdin
:
$ seq 1 500000 | ruby -lne 'BEGIN{s=0}
s+=$_.to_i
END{puts s} '
125000250000
到目前为止一切顺利。
现在将终值从 500,000 增加到 5,000,000:
$ ruby -e 's=0
for i in 1..5000000
s+=i
end
puts s'
12500002500000 <=== CORRECT
$ seq 1 5000000 | ruby -lne 'BEGIN{s=0}
s+=$_.to_i
END{puts s} '
500009500025 <=== WRONG!
它产生不同的总和。
awk
和 perl
都以相同的序列产生正确的结果:
$ seq 1 5000000 | awk '{s+=} END{print s}'
12500002500000
$ seq 1 5000000 | perl -nle '$s+=$_; END{print $s}'
12500002500000
为什么 ruby 产生的总和不正确?我不认为它是溢出,因为 awk
和 perl
在同一输入上正常工作。
结论:
感谢 David Aldridge 诊断此问题。
OS X 和 BSD
示例seq
转换为 1,000,000 的浮点输出,而 GNUseq
支持任意精度整数。 OS Xseq
作为大于 1,000,000 的整数来源是无用的。 OS X:$ seq 999999 1000002 999999 1e+06 1e+06 1e+06
ruby 方法
.to_i
静默地将部分字符串转换为整数,在本例中就是 'bug'。示例:irb(main):002:0> '5e+06'.to_i #=> 5
脚本中的 'correct' 行要么使用
$_.to_f.to_i
来使用浮点数,要么使用Integer($_)
以使脚本不会静默失败。awk
和perl
将 5e+06 解析为浮点数,而ruby
不隐式:$ echo '5e+06' | awk '{print +0}' 5000000 $ echo '5e+06' | ruby -lne 'print $_.to_i+0' 5
感谢 Stefan Schüßler 就
.to_i
行为打开 Ruby feature request。
我不确定这是 100% 的答案,但我注意到:
seq 500000 500001 | ruby -lne 'BEGIN{}
puts $_
END{} '
500000
500001
...但是...
seq 5000000 5000001 | ruby -lne 'BEGIN{}
puts $_
END{} '
5e+06
5e+06
... 所以 #to_i 将值转换为整数所采用的 "relaxed" 方法仍然有效 ...
seq 5000000 5000001 | ruby -lne 'BEGIN{}
puts $_.to_i
END{} '
5
5
...但是更严格的#to_int不会
seq 5000000 5000001 | ruby -lne 'BEGIN{}
puts $_.to_int
END{} '
-e:2:in `<main>': undefined method `to_int' for "5e+06":String (NoMethodError)
编辑:我也注意到了:
seq 5000000 5000001
5e+06
5e+06
因此必须将 -f
标志传递给 seq 以获得整数格式。
再次编辑:
最终答案:
seq -f %f 1 5000000 | ruby -lne 'BEGIN{s=0}
s+=$_.to_i
END{puts s} '
12500002500000
为了解释电子符号输出,seq
的 OS X 手册页提供了一些见解:
Use a printf(3) style format to print each number. [...] The default is
%g
.
因此,seq
的输出等同于Ruby的:
sprintf('%g', 100000)
#=> "100000"
sprintf('%g', 1000000)
#=> "1e+06"