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 的解释,这对所有人来说可能并不明显:这只是因为 collectmap 长四个字符,但功能相同. 只是为了扮演魔鬼的拥护者,我个人认为collect 更具可读性和自然性——“收集”记录并对它们做 X 的想法对我来说比“映射”记录和做更自然X给他们。 【参考方案1】:

没有区别,实际上map在C中实现为rb_ary_collectenum_collect(例如,map在数组和任何其他枚举上都有区别,但@987654328之间没有区别@ 和 collect)。


为什么mapcollect 都存在于Ruby 中? map 函数在不同语言中有许多命名约定。 Wikipedia provides an overview:

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

Ruby 为 Smalltalk 世界的程序员提供了一个别名,让他们有宾至如归的感觉。


为什么数组和枚举有不同的实现方式?枚举是一种通用的迭代结构,这意味着 Ruby 无法预测下一个元素可能是什么(你可以定义无限枚举,参见Prime 示例)。因此它必须调用一个函数来获取每个连续的元素(通常这将是each 方法)。

数组是最常见的集合,因此优化它们的性能是合理的。由于 Ruby 非常了解数组的工作原理,因此它不必调用 each,而只能使用简单的 pointer manipulation,这明显更快。

许多 Array 方法也存在类似的优化,例如 zipcount

【讨论】:

@Mark Reed 但是,不是来自 SmallTalk 的程序员会因为有两个不同的函数而被证明只是别名而感到困惑。它会引起类似上述 OP 的问题。 @SasQ 我不反对——我认为如果只有一个名字会更好。但是 Ruby 中还有很多其他别名,别名的一个特点是在操作 collectdetectinject 之间有一个很好的命名并行i>、rejectselect(也称为 mapfindreducereject(无别名)和 find_all)。 确实如此。显然,Ruby 在更多场合使用别名/同义词。例如,可以使用countlengthsize 检索数组中的元素数。数组的同一属性使用不同的词,但是通过这种方式,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】:

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

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中地图和收集之间的区别?的主要内容,如果未能解决你的问题,请参考以下文章

Ruby中的每个方法和收集方法有啥不同[重复]

gc之四--Minor GCMajor GC和Full GC之间的区别

ruby 从字符串中收集电子邮件的ruby脚本

ruby 红宝石中的文本收集器

使用自己的地图,如谷歌地图。当访问者点击地图上的点时收集和处理数据

ruby huboard的当前数据收集代码