我记得曾经一度骂过在Python中连接字符串。有人告诉我,在Python中创建一个字符串列表并在以后加入它们会更有效。我把这种做法带到了JavaScript和Ruby中,虽然我不确定它在后者中是否具有相同的好处。
任何人都可以告诉我,加入一个字符串数组并调用它是否更有效(资源和执行):加入它们或者根据需要在Ruby编程语言中连接一个字符串?
感谢。
答案 0 :(得分:30)
使用Benchmark课程自行尝试。
require "benchmark"
n = 1000000
Benchmark.bmbm do |x|
x.report("concatenation") do
foo = ""
n.times do
foo << "foobar"
end
end
x.report("using lists") do
foo = []
n.times do
foo << "foobar"
end
string = foo.join
end
end
这会产生以下输出:
Rehearsal -------------------------------------------------
concatenation 0.300000 0.010000 0.310000 ( 0.317457)
using lists 0.380000 0.050000 0.430000 ( 0.442691)
---------------------------------------- total: 0.740000sec
user system total real
concatenation 0.260000 0.010000 0.270000 ( 0.309520)
using lists 0.310000 0.020000 0.330000 ( 0.363102)
因此,在这种情况下,连接似乎更快一些。根据您的用例对您的系统进行基准测试。
答案 1 :(得分:8)
有趣,基准测试给出了令人惊讶的结果(除非我做错了):
require 'benchmark'
N = 1_000_000
Benchmark.bm(20) do |rep|
rep.report('+') do
N.times do
res = 'foo' + 'bar' + 'baz'
end
end
rep.report('join') do
N.times do
res = ['foo', 'bar', 'baz'].join
end
end
rep.report('<<') do
N.times do
res = 'foo' << 'bar' << 'baz'
end
end
end
给出
jablan@poneti:~/dev/rb$ ruby concat.rb
user system total real
+ 1.760000 0.000000 1.760000 ( 1.791334)
join 2.410000 0.000000 2.410000 ( 2.412974)
<< 1.380000 0.000000 1.380000 ( 1.376663)
join
原来是最慢的。它可能与创建数组有关,但这就是你必须要做的事情。
哦,顺便说一句,
jablan@poneti:~/dev/rb$ ruby -v
ruby 1.9.1p378 (2010-01-10 revision 26273) [i486-linux]
答案 2 :(得分:4)
是的,这是相同的原则。我记得一个ProjectEuler拼图,我在这两个方面都尝试过,调用join更快。
如果查看Ruby源代码,则连接在C中实现,它将比连接字符串(没有中间对象创建,没有垃圾收集)快得多:
/*
* call-seq:
* array.join(sep=$,) -> str
*
* Returns a string created by converting each element of the array to
* a string, separated by <i>sep</i>.
*
* [ "a", "b", "c" ].join #=> "abc"
* [ "a", "b", "c" ].join("-") #=> "a-b-c"
*/
static VALUE
rb_ary_join_m(argc, argv, ary)
int argc;
VALUE *argv;
VALUE ary;
{
VALUE sep;
rb_scan_args(argc, argv, "01", &sep);
if (NIL_P(sep)) sep = rb_output_fs;
return rb_ary_join(ary, sep);
}
其中rb_ary_join是:
VALUE rb_ary_join(ary, sep)
VALUE ary, sep;
{
long len = 1, i;
int taint = Qfalse;
VALUE result, tmp;
if (RARRAY(ary)->len == 0) return rb_str_new(0, 0);
if (OBJ_TAINTED(ary) || OBJ_TAINTED(sep)) taint = Qtrue;
for (i=0; i<RARRAY(ary)->len; i++) {
tmp = rb_check_string_type(RARRAY(ary)->ptr[i]);
len += NIL_P(tmp) ? 10 : RSTRING(tmp)->len;
}
if (!NIL_P(sep)) {
StringValue(sep);
len += RSTRING(sep)->len * (RARRAY(ary)->len - 1);
}
result = rb_str_buf_new(len);
for (i=0; i<RARRAY(ary)->len; i++) {
tmp = RARRAY(ary)->ptr[i];
switch (TYPE(tmp)) {
case T_STRING:
break;
case T_ARRAY:
if (tmp == ary || rb_inspecting_p(tmp)) {
tmp = rb_str_new2("[...]");
}
else {
VALUE args[2];
args[0] = tmp;
args[1] = sep;
tmp = rb_protect_inspect(inspect_join, ary, (VALUE)args);
}
break;
default:
tmp = rb_obj_as_string(tmp);
}
if (i > 0 && !NIL_P(sep))
rb_str_buf_append(result, sep);
rb_str_buf_append(result, tmp);
if (OBJ_TAINTED(tmp)) taint = Qtrue;
}
if (taint) OBJ_TAINT(result);
return result;
}
答案 3 :(得分:3)
我刚刚读到这个。 Attahced是一个谈论它的链接。
据我所知,在Python和Java字符串中,不可变对象与数组不同,而在Ruby中,字符串和数组彼此可变。使用String.concat或&lt;&lt;之间的速度差异可能很小。形成字符串与Array.join的方法,但它似乎不是一个大问题。
我认为链接会比我更好地解释这个。
谢谢,
马丁
答案 4 :(得分:0)
” 问题是整个数据堆。在他的第一种情况下,他有两种类型的数据存储:(1)他的CSV文件中每行的临时字符串,固定引号和类似的东西,以及(2)包含所有内容的巨型字符串。如果每个字符串是1k并且有5,000行...
场景一:用小字符串构建一个大字符串
临时字符串:5兆(5,000k) 巨大的字符串:5兆(5,000k) 总计:10兆(10,000k) 戴夫的改进脚本将大量字符串替换为数组。他保留了临时字符串,但将它们存储在一个数组中。该数组最终只会花费5000 * sizeof(VALUE)而不是每个字符串的完整大小。通常,VALUE是四个字节。
场景二:在数组中存储字符串
字符串:5兆(5,000k) 大规模阵列:20k
然后,当我们需要制作一个大字符串时,我们称之为join。现在我们达到了10兆,突然所有这些字符串变成了临时字符串,它们都可以立即被释放。最终这是一个巨大的成本,但它比一直消耗资源的渐进式渐强效率要高得多。 “
http://viewsourcecode.org/why/hacking/theFullyUpturnedBin.html
^在内存/垃圾收集性能方面实际上更好的是将操作延迟到最后,就像我在Python中教过的那样。之所以开始,你会在最后获得一大块分配并立即释放对象。
答案 5 :(得分:0)
@jergason 的 answer 显示连接速度稍快,但这是 because the shovel operator <<
is allowed to modify the original string。
如果我们使用 frozen_string_literal: true
在顶部运行相同的基准测试,您会得到以下结果:
Rehearsal -------------------------------------------------
using lists 0.140621 0.015146 0.155767 ( 0.308191)
concatenation Traceback (most recent call last):
8: from main.rb:5:in `<main>'
7: from /usr/lib/ruby/2.5.0/benchmark.rb:255:in `bmbm'
6: from /usr/lib/ruby/2.5.0/benchmark.rb:255:in `inject'
5: from /usr/lib/ruby/2.5.0/benchmark.rb:255:in `each'
4: from /usr/lib/ruby/2.5.0/benchmark.rb:257:in `block in bmbm'
3: from /usr/lib/ruby/2.5.0/benchmark.rb:293:in `measure'
2: from main.rb:16:in `block (2 levels) in <main>'
1: from main.rb:16:in `times'
main.rb:17:in `block (3 levels) in <main>': can't modify frozen String (FrozenError)
如果您更新串联基准以使用 +=
而不是 <<
,您会发现串联基准永远不会终止。
因此,Array#join
比多次调用 +=
快。