在Ruby中映射和收集的区别?

我在谷歌上搜索了一下,得到了不一致/矛盾的意见——在Ruby/Rails中对数组执行map和执行collect之间实际上有任何区别吗?

文档似乎没有任何建议,但可能有不同的方法或性能?

129648 次浏览

有人告诉我它们是相同的。

实际上,它们在ruy-doc.org的同一个地方都有记录:

http://www.ruby-doc.org/core/classes/Array.html#M000249

  • 必要。收集{|item| block}→new_ary
  • 必要。映射{|item| block}→new_ary
  • 必要。Collect→an_enumerator
  • 必要。Map→an_enumerator
为self的每个元素调用一次block。 创建一个包含块返回值的新数组。 参见Enumerable#collect.
如果没有给出block,则返回枚举数
a = [ "a", "b", "c", "d" ]
a.collect {|x| x + "!" }   #=> ["a!", "b!", "c!", "d!"]
a                          #=> ["a", "b", "c", "d"]

没有区别,事实上map在C中实现为rb_ary_collectenum_collect(例如。数组上的map和其他enum上的map有区别,但mapcollect没有区别)。


map函数在不同的语言中有许多命名约定。维基百科提供了一个概述:

map函数起源于函数式编程语言,但今天在许多过程式、面向对象和多范式语言中也被支持(或可以被定义):在c++的标准模板库中,它被称为transform,在c#(3.0)的LINQ库中,它被作为扩展方法提供,称为Select。Map也是Perl、Python和Ruby等高级语言中经常使用的操作;该操作在这三种语言中都被称为mapRuby中还提供了映射的collect别名(来自Smalltalk)[强调我的]。Common Lisp提供了一系列类似地图的函数;对应于这里描述的行为的是mapcar (-car表示使用CAR操作进行访问)。

Ruby为Smalltalk世界的程序员提供了一个别名,让他们感觉更自在。


枚举是一个广义迭代结构,这意味着Ruby无法预测下一个元素是什么(你可以定义无限枚举,参见主要的的例子)。因此它必须调用一个函数来获取每个连续的元素(通常这将是each方法)。

数组是最常见的集合,因此优化它们的性能是合理的。由于Ruby非常了解数组的工作方式,因此它不必调用each,而只能使用简单的指针操作,这要快得多。

类似的优化存在于许多数组方法,如zipcount

Ruby将方法array# map别名为array# collect;它们可以互换使用。(Ruby和尚)

换句话说,相同的源代码:

               static VALUE
rb_ary_collect(VALUE ary)
{
long i;
VALUE collect;


RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length);
collect = rb_ary_new2(RARRAY_LEN(ary));
for (i = 0; i < RARRAY_LEN(ary); i++) {
rb_ary_push(collect, rb_yield(RARRAY_AREF(ary, i)));
}
return collect;
}

< a href = " http://ruby-doc.org/core-2.2.0/Array.html method-i-map " > http://ruby-doc.org/core-2.2.0/Array.html method-i-map < / >

我做了一个基准测试来尝试回答这个问题,然后找到了这篇文章,所以这里是我的发现(与其他答案略有不同)

下面是基准代码:

require 'benchmark'


h = { abc: 'hello', 'another_key' => 123, 4567 => 'third' }
a = 1..10
many = 500_000


Benchmark.bm do |b|
GC.start


b.report("hash keys collect") do
many.times do
h.keys.collect(&:to_s)
end
end


GC.start


b.report("hash keys map") do
many.times do
h.keys.map(&:to_s)
end
end


GC.start


b.report("array collect") do
many.times do
a.collect(&:to_s)
end
end


GC.start


b.report("array map") do
many.times do
a.map(&:to_s)
end
end
end

我得到的结果是:

                   user     system      total        real
hash keys collect  0.540000   0.000000   0.540000 (  0.570994)
hash keys map      0.500000   0.010000   0.510000 (  0.517126)
array collect      1.670000   0.020000   1.690000 (  1.731233)
array map          1.680000   0.020000   1.700000 (  1.744398)

也许别名不是免费的?

collectcollect!方法是mapmap!的别名,所以它们可以互换使用。下面是一个简单的确认方法:

Array.instance_method(:map) == Array.instance_method(:collect)
=> true

#collect实际上是#map的别名。这意味着这两种方法可以互换使用,并产生相同的行为。