将一个数组中的每个元素都乘以另一个数组中的每个元素

4
在Ruby中,如何将一个数组中的每个元素与另一个数组中的每个元素相乘,使得结果为:
a = [1,2,3]

b = [4,5,6]

c = a*b = [4,5,6,8,10,12,12,15,18]

4
我现在明白你为什么这么快地选了一个答案。那个问题的作者在你发布问题几分钟后就要求你这么做!他这样做非常不礼貌。如果这个问题是由经验丰富的SO会员发布的,他就不会这样做,因为他知道他的请求不会落空。我建议将来至少等待几个小时,这样你就不会阻止其他人发布答案或打断那些仍在努力回答的人。没有什么赶时间的... - Cary Swoveland
@CarySwoveland,楼主感谢您的回答,并表示问题已得到解决。回答者随即询问楼主是否能够接受该回答,甚至提供了一个链接,详细说明了如何接受回答。我认为这里没有任何不当行为。 - Stefan
4个回答

10

要得到一个良好的抽象,可以使用product来获取笛卡尔积:

a.product(b).map { |aa, bb| aa * bb }

每当我看到像这样的代码(至少每月一次),我总是想知道为什么Ruby核心中要引入Enumerable#map_reduce? :) - Aleksei Matiushkin
4
@CarySwoveland 我确定这是遗留问题;顺便说一下,你可以随时使用 a.enum_for(:product, b) - Aleksei Matiushkin
1
谢谢,mudsie,er @AlekseiMatiushkin。这很有道理,感谢您提供的代码(我已经保存好了)。 - Cary Swoveland
1
顺便提一下,zip 更糟糕 - 当给出一个块时,它只返回(无用的)nil - Stefan
@shirakia请更改已接受的答案,这个是正确的。 - noraj
显示剩余3条评论

5

这个解决方案利用Matrix方法来计算(然后展开)两个向量的外积

require 'matrix'

(Matrix.column_vector(a) * Matrix.row_vector(b)).to_a.flatten
  #=> [4, 5, 6, 8, 10, 12, 12, 15, 18]

与其他两个回答相同,这会生成一个临时数组,当展开该数组(如果尚未展开)时,其中包含a.size**2个元素。如果a太大而导致存储问题,您可以使用一对枚举器代替:
a.each_with_object([]) { |aa,arr| b.each { |bb| arr << aa*bb } }
  #=> [4, 5, 6, 8, 10, 12, 12, 15, 18]

以下是枚举器。

enum_a = a.each_with_object([])
  #=> #<Enumerator: [1, 2, 3]:each_with_object([])>
aa, arr = enum_a.next
  #=> [1, []]
aa, arr = enum_a.next
  #=> [2, []]
...

enum_b = b.each
  #=> #<Enumerator: [4, 5, 6]:each>
bb = enum_b.next
  #=> 4
bb = enum_b.next
  #=> 5
...

请查看Enumerator#next,这是枚举器将元素传递给它们的块的方法。
方法Enumerable#each_with_object非常方便,而且并不像最初看起来那么复杂。大多数情况下,它只是从以下代码中节省了两行。
arr = []
a.each { |aa| b.each { |bb| arr << aa*bb } }
arr

1
“叉积都是用C语言实现的” - 你确定吗?matrix.rb的源代码看起来好像是用Ruby实现的。 - Stefan
1
@Stefan,当我阅读你提供的链接中的代码时,我简直惊呆了。为什么?不是为什么羽毛,而是为什么要在Ruby中编写每一个Matrix方法?在Ruby中编写数值计算方法,如Matrix#lup_decomposition,这个想法让人难以置信。我原本以为会使用优化的公共领域矩阵库(带有Ruby包装器)来编写此类方法。那样做也比重新发明轮子容易得多。 - Cary Swoveland

2

尝试以下操作:

a.product(b).map { |x| x.inject(&:*) }

惊人的是,以下方法也可以解决这个问题。
a.map { |x| b.map(&x.method(:*)) }.flatten

前者是@Amadan给出的答案的浅层复制,后者是[不是最好的]被接受答案的精确复制。被踩了。 - Aleksei Matiushkin
使用 (:*) 在你的第一个答案中,使用 flat_map 在你的第二个答案中。 - Sagar Pandya
@SagarPandya 我个人不喜欢向 reduce 传递一个符号参数,因为这种方法只允许这样做,而使用 Symbol#to_proc 则是整个 Ruby 核心广泛采用的常见方法。 - Aleksei Matiushkin

-3

这可能不是很美观,但会返回你想要的结果。

a.map{|aa| b.map{|bb| bb * aa}}.flatten


5
我永远不会给任何在几分钟内向新手 SO 提问并得到答案后请求点赞的人点赞。真丢脸! - Cary Swoveland
1
感谢你的建议。你是对的。我只是认为告诉如何使用StackOverflow是一种很好的行为。 - shirakia
新用户有时不知道答案可以被接受。因此,如果一个新用户说“谢谢,这个有效”(即解决了问题),但没有接受答案,那么要求他们这样做是完全可以的。 - Stefan
我给你点赞 - 你在5分钟内回答了问题,你的解决方案有效并且解决了原帖作者的问题。 - Stefan
@Stefan,最后,让我问你一下。你是否曾经回答过一个明显新手提出的问题并说过:“如果你发现我回答了你的问题,请选择我的答案”?如果答案是“没有”,那么你应该问问自己这种行为与你上面的评论是否相符。就是这样! - Cary Swoveland
显示剩余7条评论

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