1

我正在用 Ruby 上一门 SaaS 课程。在一个练习中,我被要求使用迭代器、块和产量来计算两个序列的笛卡尔积。

通过纯粹的猜测和错误,我最终得到了这个,它似乎有效。但我不确定如何。我似乎了解基本块和产量用法,但是这个?一点也不。

class CartProd
  include Enumerable
  def initialize(a,b)
        @a = a
        @b = b
  end
  def each
        @a.each{|ae|
                @b.each{|be|
                        yield [ae,be]
                }
        }
  end
end

请给像我这样的菜鸟一些解释,好吗?

(PS:我将所需的类名更改为 CartProd,因此学习该课程的人无法通过谷歌搜索轻松找到响应)

4

3 回答 3

7

让我们一步一步地构建它。我们将通过将其从类上下文中删除来简化一些事情。

对于这个例子,很直观地认为迭代器是传统 for 循环的更强大的替代品。

所以首先这是一个for循环版本:

seq1 = (0..2)
seq2 = (0..2)
for x in seq1
  for y in seq2
    p [x,y] # shorthand for puts [x, y].inspect
  end
end  

现在让我们用更多 Ruby 惯用的迭代器样式替换它,显式提供要执行的块(即do...end块):

seq1.each do |x|
  seq2.each do |y|
    p [x,y]
  end
end

到目前为止,一切顺利,您已经打印出了笛卡尔积。现在你的作业也要求你使用yield。的重点yield是“让步执行”,即将控制权临时传递给另一个代码块(可选地传递一个或多个参数)。

因此,尽管对于这个玩具示例来说这并不是真正必要的,但您可以直接打印该值,而不是像上面那样直接打印该yield值,并让调用者提供一个接受该值并打印它的块。

这可能看起来像这样:

 def prod(seq1, seq2)
    seq1.each do |x|
      seq2.each do |y|
        yield [x,y]
      end
    end
  end

像这样调用:

prod (1..2), (1..2) do |prod| p prod end

为内部循环的yield每次运行提供产品,并且生成的值由调用者提供的块打印。

于 2012-10-06T13:49:48.383 回答
0

What exactly do you not understand here? You've made an iterator that yields all possible pairs of elements. If you pass CartProd#each a block, it will be executed a.length*b.length times. It's like having two different for cycles folded one into another in any other programming language.

于 2012-10-06T12:31:58.480 回答
0

yield只是将控制权传递给作为方法调用的一部分传入的代码块。yield 关键字之后的值作为参数传递到块中。一旦块完成执行,它就会传回控制权。

因此,在您的示例中,您可以像这样调用#each:

CartProd.new([1, 2], [3, 4]).each do |pair|
  # control is yielded to this block
  p pair
  # control is returned at end of block
end

这将输出每对值。

于 2012-10-06T13:29:43.853 回答