Ruby中地图和收集之间的区别?
Posted
技术标签:
【中文标题】Ruby中地图和收集之间的区别?【英文标题】:Difference between map and collect in Ruby? 【发布时间】:2011-07-12 09:39:01 【问题描述】:我在谷歌上搜索了这个并得到了零散/矛盾的意见 - 在 Ruby/Rails 中的数组上执行 map
和执行 collect
之间实际上有什么区别吗?
docs 似乎没有任何建议,但在方法或性能上可能存在差异吗?
【问题讨论】:
map
首选Code Golf。
作为 CodeGolf 首选 map
的解释,这对所有人来说可能并不明显:这只是因为 collect
比 map
长四个字符,但功能相同.
只是为了扮演魔鬼的拥护者,我个人认为collect
更具可读性和自然性——“收集”记录并对它们做 X 的想法对我来说比“映射”记录和做更自然X给他们。
【参考方案1】:
没有区别,实际上map
在C中实现为rb_ary_collect
和enum_collect
(例如,map
在数组和任何其他枚举上都有区别,但@987654328之间没有区别@ 和 collect
)。
为什么map
和collect
都存在于Ruby 中? map
函数在不同语言中有许多命名约定。 Wikipedia provides an overview:
map 函数起源于函数式编程语言,但现在许多过程、面向对象和多范式语言也支持(或可能定义):在 C++ 的标准模板库中,它被称为
transform
,在C# (3.0) 的 LINQ 库,它作为扩展方法提供,称为Select
。 Map 也是 Perl、Python 和 Ruby 等高级语言中常用的操作;在所有这三种语言中,该操作都称为map
。 Ruby(来自 Smalltalk)也提供了映射的collect
别名 [强调我的]。 Common Lisp 提供了一系列类似地图的函数;与此处描述的行为相对应的行为称为mapcar
(-car 表示使用 CAR 操作进行访问)。
Ruby 为 Smalltalk 世界的程序员提供了一个别名,让他们有宾至如归的感觉。
为什么数组和枚举有不同的实现方式?枚举是一种通用的迭代结构,这意味着 Ruby 无法预测下一个元素可能是什么(你可以定义无限枚举,参见Prime 示例)。因此它必须调用一个函数来获取每个连续的元素(通常这将是each
方法)。
数组是最常见的集合,因此优化它们的性能是合理的。由于 Ruby 非常了解数组的工作原理,因此它不必调用 each
,而只能使用简单的 pointer manipulation,这明显更快。
许多 Array 方法也存在类似的优化,例如 zip
或 count
。
【讨论】:
@Mark Reed 但是,不是来自 SmallTalk 的程序员会因为有两个不同的函数而被证明只是别名而感到困惑。它会引起类似上述 OP 的问题。 @SasQ 我不反对——我认为如果只有一个名字会更好。但是 Ruby 中还有很多其他别名,别名的一个特点是在操作 collect、detect、inject 之间有一个很好的命名并行i>、reject 和 select(也称为 map、find、reduce、reject(无别名)和 find_all)。 确实如此。显然,Ruby 在更多场合使用别名/同义词。例如,可以使用count
、length
或size
检索数组中的元素数。数组的同一属性使用不同的词,但是通过这种方式,Ruby 使您能够为您的代码选择最合适的词:您想要收集的项目的 number 个,数组的长度,或结构的当前大小。本质上,它们都是相同的,但是选择正确的词可能会使您的代码更易于阅读,这是该语言的一个很好的特性。【参考方案2】:
有人告诉我它们是一样的。
实际上它们记录在 ruby-doc.org 下的同一个地方:
http://www.ruby-doc.org/core/classes/Array.html#M000249
ary.collect |item|块 → new_ary ary.map |项目|块 → new_ary ary.collect → an_enumerator ary.map → an_enumerator为 self 的每个元素调用一次块。 创建一个包含块返回值的新数组。 另请参阅 Enumerable#collect。 如果没有给出块,则返回一个枚举器。
a = [ "a", "b", "c", "d" ] a.collect |x| x + "!" #=> ["a!", "b!", "c!", "d!"] a #=> ["a", "b", "c", "d"]
【讨论】:
只是为了彻底:http://www.ruby-doc.org/core/classes/Enumerable.html#method-i-map【参考方案3】:collect
和 collect!
方法是 map
和 map!
的别名,因此它们可以互换使用。这是一个简单的确认方法:
Array.instance_method(:map) == Array.instance_method(:collect)
=> true
【讨论】:
【参考方案4】:我做了一个基准测试来尝试回答这个问题,然后找到了这篇文章,所以这是我的发现(与其他答案略有不同)
这是基准代码:
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)
也许别名不是免费的?
【讨论】:
我不确定这些差异是否显着。在重新运行时,我得到了不同的速度结果(即使您的哈希收集似乎较慢,但您的数组收集似乎更快)【参考方案5】:Ruby 将方法 Array#map 别名为 Array#collect;它们可以互换使用。 (红宝石和尚)
换句话说,相同的源代码:
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;
http://ruby-doc.org/core-2.2.0/Array.html#method-i-map
【讨论】:
我希望文档明确说明它们是别名。目前它们只是相互引用,两者的描述略有不同。【参考方案6】:#collect
实际上是#map
的别名。这意味着这两种方法可以互换使用,并产生相同的行为。
【讨论】:
以上是关于Ruby中地图和收集之间的区别?的主要内容,如果未能解决你的问题,请参考以下文章
gc之四--Minor GCMajor GC和Full GC之间的区别