gonzalo2000
gonzalo2000

Reputation: 648

iterate with a method within ruby class

Class Image initializes with an array of 0's and 1's. I have method transform, such that

[[0,0,0],
 [0,1,0],
 [0,0,0]]

returns

[[0,1,0],
 [1,1,1],
 [0,1,0]]

I want to implement method blur(n), which iterates n times with transform, such calling blur(2) with

[[0,0,0,0,0,0,0,0,0],
 [0,0,0,0,0,0,0,0,0],
 [0,0,0,0,1,0,0,0,0],
 [0,0,0,0,0,0,0,0,0],
 [0,0,0,0,0,0,0,0,0]]

returns

[[0,0,0,0,1,0,0,0,0],
 [0,0,0,1,1,1,0,0,0],
 [0,0,1,1,1,1,1,0,0],
 [0,0,0,1,1,1,0,0,0],
 [0,0,0,0,1,0,0,0,0]]

I'm trying to use transform iteratively to achieve this, but I'm getting undefined method 'map' for #<Context::Image:0x000000012eb020> when calling blur with an instance of Image. How can I iterate over each successive transformation, such that blur returns the latest version with the maximum n transformations?

class Image
  attr_accessor :array

  def initialize(array)
    self.array = array
  end

  def output_image
    self.array.each do |item|
      puts item.join
    end
  end

  def transform #changes adjacent a 1's adjacent 0's into 1
    cloned = self.array.map(&:clone)

    #scan original array for 1; map crosses into clone if found
    self.array.each.with_index do |row, row_index|
      row.each.with_index do |cell, col|
        if cell == 1
          cloned[row_index][col+1] = 1 unless col+1 >= row.length #copy right
          cloned[row_index+1][col] = 1 unless row_index+1 >= cloned.length # copy down
          cloned[row_index][col-1] = 1 unless col.zero? # copy left
          cloned[row_index-1][col] = 1 unless row_index.zero? #copy up
        end
      end
    end
    cloned
  end

  def blur(n) #should call transform iteratively n times
    blurred = Image.new(self)
    n.times do
      blurred = blurred.transform
    end
    blurred
  end

end

Upvotes: 3

Views: 174

Answers (2)

Cary Swoveland
Cary Swoveland

Reputation: 110675

You could use the Matrix class.

require 'matrix'    

class Matrix
  def el(r,c)
    if r < 0 || r >= row_count || c < 0 || c >= column_count
      0
    else
      self[r,c]
    end
  end

  def transform
    Matrix.build(row_count, column_count) { |r,c|
      [el(r,c), el(r-1,c), el(r+1,c), el(r,c-1), el(r,c+1)].max }
  end
end

Given a row-column pair, r, c, the helper method el returns 0 if the row or column is outside the bounds of the matrix and the value at [r,c] otherwise.

nrows = 5
ncols = 5

m = Matrix.build(nrows, ncols) { |r,c| (r==nrows/2 && c==ncols/2) ? 1 : 0 }
  #=> Matrix[[0, 0, 0, 0, 0],
  #          [0, 0, 0, 0, 0],
  #          [0, 0, 1, 0, 0],
  #          [0, 0, 0, 0, 0],
  #          [0, 0, 0, 0, 0]] 
m = m.transform
  #=> Matrix[[0, 0, 0, 0, 0],
  #          [0, 0, 1, 0, 0],
  #          [0, 1, 1, 1, 0],
  #          [0, 0, 1, 0, 0],
  #          [0, 0, 0, 0, 0]] 
m = m.transform
  #   Matrix[[0, 0, 1, 0, 0],
  #          [0, 1, 1, 1, 0],
  #          [1, 1, 1, 1, 1],
  #          [0, 1, 1, 1, 0],
  #          [0, 0, 1, 0, 0]]
m.to_a
  #=>       [[0, 0, 1, 0, 0],
  #          [0, 1, 1, 1, 0],
  #          [1, 1, 1, 1, 1],
  #          [0, 1, 1, 1, 0],
  #          [0, 0, 1, 0, 0]] 

Upvotes: 1

Keith Bennett
Keith Bennett

Reputation: 4970

map is a method available to an Array, but not to your custom class Image.

I suggest calling map on your instance variable @array instead. Then, when your transforms are completed, create a new Image instance with that transformed array.

Below is an example of code that should work. Note that transform and blur take input arrays as parameters, so they do not rely on any instance state. Therefore, I've made them class methods instead of instance methods. This allows your users to use them without having to create an instance, if all they want to do is the array transformation. It also makes those methods easy to extract to a module in future refactorings. I've added an instance method, blurred_image, which applies the transformation to the instance and returns a new Image instance.

def self.transform(input_array) #changes adjacent a 1's adjacent 0's into 1
    cloned = input_array.map(&:clone)

    #scan original array for 1; map crosses into clone if found
    input_array.each.with_index do |row, row_index|
      row.each.with_index do |cell, col|
        if cell == 1
          cloned[row_index][col+1] = 1 unless col+1 >= row.length #copy right
          cloned[row_index+1][col] = 1 unless row_index+1 >= cloned.length # copy down
          cloned[row_index][col-1] = 1 unless col.zero? # copy left
          cloned[row_index-1][col] = 1 unless row_index.zero? #copy up
        end
      end
    end
    cloned
  end

  def self.blur(input_array, transform_count) #should call transform iteratively n times
    blurred = input_array
    transform_count.times { blurred = transform(blurred) }
    Image.new(blurred)
  end

  def blurred_image(transform_count)
    self.class.new(self.class.blur(array, transform_count))
  end

Upvotes: 1

Related Questions