如何理解Ruby中的.each和.map方法?

5
我很困惑如何理解mapeach之间的区别,以及使用它们的时机和场合。
我阅读了《What does map do?》和《Ruby Iterators》,但还需要一些澄清。
如果我有以下内容:
 z = [1,2,3].map {|x| x + 1}

map函数对于数组z中的每个元素都加一,但是如果不加上!符号,它并不会改变原始数组。

另一方面:

y = [1,2,3].each {|x| x + 1}

返回结果是 [1,2,3]。对我来说这有点困惑,因为:

names = ['danil', 'edmund']
names.each { |name| puts name + ' is a programmer' }

返回值:

Danil is a programmer
Edmund is a programmer

在我的第二个例子中,为什么不能将每个数组元素增加1,而在最后一个例子中,一个字符串被附加到数组中的每个元素上?

所有的功劳都归于Speransky Danil,我从他那里得到了这些例子。


3
"returns"“Danil is a programmer” - 不对。你可能感到困惑了。这不是返回值,而是该代码块的__副作用__(将其打印到标准输出)。.each 的返回值仍然是原始的可枚举对象(在这两行之后可以看到它)。 - Sergio Tulentsev
5个回答

6
map方法接受一个enum和一个代码块,然后迭代执行一些逻辑。在您的情况下,这个逻辑是x+1。正如您所说,除非使用!,否则它不会更改任何内容。

each只是返回被调用的数组。

让我们举个例子:

names = ["bob"]

如果我们执行以下操作:
names.each{|names| names + "somestring"}

输出仍然是["bob"]。你的第二个例子之所以不同,是因为有puts语句。

作为练习,尝试执行以下操作:

y = [1,2,3].each {|x| puts x + 1}

你将获得:

2
3
4
 [1,2,3]

3
简而言之:当我想改变集合,对其应用转换并最终得到不同的结果时,我使用map。如果我只需要访问集合中的每个元素,则使用each
关键点是:如果您想在数组(实际上是可枚举对象)上应用转换,则应使用map。否则,如果您不需要更改数组,则可以简单地使用each
请注意,在下面的代码中,您没有改变数组,而是利用本地字符串打印每个带后缀的字符串。
names = ['danil', 'edmund']
names.each { |name| puts name + ' is a programmer' }

显然,你也可以使用map来实现同样的效果,但在这种情况下不需要它,你需要使用each来打印每个元素。代码如下:

names = ['danil', 'edmund']
names.map! { |name| name + ' is a programmer' }
# or names = names.map { |name| name + ' is a programmer' }
name.each { |name| puts name }

1
这个问题在 Ruby 的文档中有多处涉及,但对于您的使用最易理解的是在数组文档中关于 each 的部分。
each { |item| block } → ary
each → Enumerator

Calls the given block once for each element in self, passing that element as a parameter. Returns the array itself.

If no block is given, an Enumerator is returned.

a = [ "a", "b", "c" ]
a.each {|x| print x, " -- " }

produces:

a -- b -- c --
请注意,它说“返回数组本身”。
将其与map进行比较:
map { |item| block } → new_ary
map → Enumerator

Invokes the given block once for each element of self.

Creates a new array containing the values returned by the block.

See also Enumerable#collect.

If no block is given, an Enumerator is returned instead.

a = [ "a", "b", "c", "d" ]
a.collect { |x| x + "!" }         #=> ["a!", "b!", "c!", "d!"]
a.map.with_index { |x, i| x * i } #=> ["", "b", "cc", "ddd"]
a                                 #=> ["a", "b", "c", "d"]
注意,它说:“创建一个新数组,其中包含块返回的值。
以下示例应有助于了解上述内容:
foo = [1,2,3]
foo.each { |i| puts i + 1 } # => [1, 2, 3]
foo.map { |i| i + 1 } # => [2, 3, 4]

# >> 2
# >> 3
# >> 4

其中# =>是块的返回值,# >>是从puts中捕获的STDOUT。

了解这些后,当您想要显示数组中的元素或提取和重用这些元素作为参数或构建内容时,请使用each。当您想将数组元素更改为其他内容时,请使用map


0

它们的区别在于each对数组中的每个元素执行操作,返回原始数组。所执行的操作可能会改变元素。

map对数组中的每个元素执行操作,并将其结果作为数组返回。


0
在第一种情况下,map

 z = [1,2,3].map {|x| x + 1}

将会对给定的数组中的每个元素执行块中的操作并返回一个新的数组,因此这里返回[2,3,4]

.each会为数组中的每个元素执行块,并且它不会改变数组中的任何内容,所以这里执行x + 1,但它不会存储在任何地方,因此在第二种情况下它只是返回数组。

现在在你发布的第三个例子中,你正在块本身中打印输出。同样,数组本身没有任何变化。


请非常小心地使用加粗文本。它相当于用大写字母大声喊叫,会造成视觉上的干扰。只有极少数情况下才适合使用加粗,因此请非常节制地使用。想象一下参考书籍,比如百科全书,你在那里看到过多少加粗文本呢? - the Tin Man

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接