Reputation: 129
I'm trying to teach myself Python by translating the code in the Mazes for Programmers book from Jamis Buck. It's a great book but it's written in Ruby and I'm getting stuck on understanding some of the Ruby code.
To understand the problem, I've included a reduced version of the codebase
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
So far so good. All of the above is easy to understand and translate to Python. Then there are the following two functions as part of the Grid class.
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
What are the last two functions here actually doing? I've found something similar here that leads me to think that the Python version will need to accept an optional lambda variable, test to see that it's not null, and if it isn't then run the code associated with the variable. The problem is that I know the intent of these functions is to be an iterator and I don't think that adding in a lambda will help.
There is a somewhat similar question already here on StackOverflow which makes me think that the answer is trivial, but I don't know enough about Ruby to be able to intuit the answer or ask Google the right questions.
Upvotes: 2
Views: 177
Reputation: 369498
Then there are the following two functions as part of the Grid class.
These aren't functions. They are methods.
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
What are the last two functions here actually doing?
The each_row
method takes a block as parameter and will successively yield
all elements of the @grid
array. @grid
is structured as an array of arrays, representing rows of cells. In other words, each_row
will successively yield
each row of the grid, i.e. it is an iterator method for rows.
The each_cell
method takes a block as parameter and will successively yield
all elements of the row arrays in the grid @grid
array. In other words, each_cell
will successively yield
each cell of the grid if it exists, i.e. it is an iterator method for cells.
The literal translation to Python would be something like this (untested):
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))
But, it just doesn't make sense to translate code from one language to another this way. Using lambdas for iteration in Python is non-idiomatic. Python uses iterators for iteration. So, instead of having each_row
and each_cell
iterator methods, you would rather have row_iterator
and cell_iterator
getters which return iterator objects for the rows and cells, so that you can then do something like:
for cell in grid.cell_iterator
instead of
grid.each_cell(lambda cell: …)
Something like this (also untested):
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
When you "translate" code from one language to another, you cannot just translate it line-by-line, statement-by-statement, expression-by-expression, subroutine-by-subroutine, class-by-class, etc. You need to re-design it from the ground up, using the patterns, practices, and idioms of the community and the types, classes, subroutines, modules, etc. from the language and its core and standard libraries.
Otherwise, you could just use a compiler. A compiler is literally defined as "a program that translates a program from one language to another language". If that is all you want to do, use a compiler. But, if you want the translated code to be readable, understandable, maintainable, and idiomatic, it can only be done by a human.
Upvotes: 4