将带有yield语句的Ruby循环迭代转换为Python

时间:2017-06-28 05:21:17

标签: python ruby

我试图通过翻译Jamis Buck的Mazes for Programmers一书中的代码来自学Python。它是一本很棒的书,但是它是用Ruby编写的,而且我很难理解一些Ruby代码。

为了解这个问题,我已经包含了代码库的简化版本

cell.rb

class Cell
  attr_reader : row, :column

def initialize(row, column)
  @row, @column = row, column
end

grid.rb

require 'cell'
class Grid
  attr_reader :rows, :columns

def initialize(rows, columns)
  @rows = rows
  @columns = columns
  @grid = prepare_grid
end

def prepare_grid
  Array.new(rows) do |row|
    Array.new(columns) do |column|
      Cell.new(row, column)
    end
  end
end

到目前为止一切顺利。以上所有内容都很容易理解并转换为Python。然后有以下两个函数作为Grid类的一部分。

def each_row
  @grid.each do |row|
    yield row
  end
end

def each_cell
  each_row do |row|
    row.each do |cell|
      yield cell if cell
    end
  end
end

这里的最后两个功能实际上是做什么的?我发现了类似here的东西,这让我觉得Python版本需要接受一个可选的lambda变量,测试看它是不是null,如果它不是'然后运行与变量关联的代码。问题是我知道这些函数的意图是成为迭代器,我不认为添加lambda会有所帮助。

在StackOverflow上已经有一个类似的问题here让我觉得答案是微不足道的,但是我不太了解Ruby能够直接得到答案或者问Google是否正确的问题。

1 个答案:

答案 0 :(得分:4)

  

然后有以下两个函数作为Grid类的一部分。

这些不是功能。它们是方法。

def each_row
  @grid.each do |row|
    yield row
  end
end

def each_cell
  each_row do |row|
    row.each do |cell|
      yield cell if cell
    end
  end
end
     

这里的最后两个功能实际上是做什么的?

each_row 方法将块作为参数,并将连续yield @grid数组的所有元素。 @grid结构为数组数组,表示单元格行。换句话说,each_row将连续yield网格的每一行,即它是行的迭代器方法。

each_cell 方法将块作为参数,并将连续yield网格@grid数组中行数组的所有元素。换句话说,each_cell将连续yield网格的每个单元格(如果存在),即它是单元格的迭代器方法。

Python的字面翻译将是这样的(未经测试):

def each_row(self, f):
  self.grid.each(lambda row: f(row))

def each_cell(self, f):
  self.each_row(lambda row: lambda cell: if cell: f(cell))

但是,以这种方式将代码从一种语言翻译成另一种语言是没有意义的。在Python中使用lambdas迭代是非惯用的。 Python使用迭代器进行迭代。因此,不是使用each_roweach_cell迭代器方法,而是使用row_iteratorcell_iterator getter来返回行和单元格的迭代器对象,这样就可以了做类似的事情:

for cell in grid.cell_iterator

而不是

grid.each_cell(lambda cell: …)

这样的事情(也是未经测试的):

def row_iterator(self):
  for row in self.grid: yield row

def cell_iterator(self):
  for row in self.row_iterator:
    for cell in row:
      if cell: yield cell

当你将代码从一种语言“翻译”到另一种语言时,你不能只是逐行,逐个语句,逐个表达式,逐个子程序,逐个类地翻译它,您需要从头开始重新设计它,使用社区的模式,实践和习惯用语以及语言及其核心和标准库中的类型,类,子例程,模块等。

否则,您可以使用编译器。编译器实际上被定义为“将程序从一种语言翻译成另一种语言的程序”。如果这就是您想要做的,请使用编译器。但是,如果您希望翻译后的代码具有可读性,可理解性,可维护性和惯用性,则只能由人工完成。