Daniel
Daniel

Reputation: 25

Ruby macro text substitution

I know macros are not possible in Ruby, however, I've run into a situation where I don't know of a workaround.

I have two methods (and I may have more) that are almost identical, and differ only in a couple variable names. Specifically, I swapped the |n| and |m| in the outer and inner upto loops.

def self.check_across
  0.upto(4) do |n|
    test = 0
    0.upto(4) { |m| if @@bingo_board[n][m] == "X" then test += 1 end }
    if test == 5 then return true end
  end
  return false
end

def self.check_down
  0.upto(4) do |m|
    test = 0
    0.upto(4) { |n| if @@bingo_board[n][m] == "X" then test += 1 end }
    if test == 5 then return true end
  end
  return false
end

In the first method I'm checking a 5x5 bingo board for a complete row of "X" values across, and the second method checks for a complete column of "X" values.

I know that in SAS you can just wrap this method in a %macro (if you know what I mean). In Ruby, I know this doesn't work, but what I'm TRYING to do is this:

def self.check(x, y)
  0.upto(4) do |x|
    test = 0
    0.upto(4) { |y| if @@bingo_board[n][m] == "X" then test += 1 end }
    if test == 5 then return true end
  end
  return false
end

example.check(n, m)    # same as .check_across
example.check(m, n)    # same as .check_down

Of course, this doesn't work since n and m would be treated as variable names that exist outside of the method.

Upvotes: 0

Views: 249

Answers (2)

the Tin Man
the Tin Man

Reputation: 160551

You might want to try this untested code:

def self.check_across
  @@bingo_board.each do |n|
    return true if n.all?{ |m| m == 'X' }
  end

  false
end

def self.check_down
  @@bingo_board.transpose.each do |m|
    return true if m.all?{ |n| n == 'X' }
  end

  false
end

transpose is a nice method that is useful for this sort of problem. all? makes it easy to see if all elements in an array are the same, and returns true if it does.


It looks like those can be DRY'd to:

def self.check_across
  @@bingo_board.any?{ |n|
    n.all?{ |m| m == 'X' }
  }
end

def self.check_down
  @@bingo_board.transpose.any?{ |m|
    m.all?{ |n| n == 'X' }
  }
end

any? is useful to find whether any element matches a condition and returns true. In this case it's whether a row or column is all 'X'.

Upvotes: 1

hjing
hjing

Reputation: 4982

You can use a block to accomplish something similar.

For example,

def check
  0.upto(4) do |i|
    test = 0
    0.upto(4) do |j|
      # hand control of how to deal with indices over to the calling method
      if yield(i,j) == 'X'
        test += 1
      end
    end
    return true if test == 5
  end
  false
end

def check_across(board)
  check do |i,j|
    # implementing logic for accessing rows
    board[i][j]
  end
end

def check_down(board)
  check do |i,j|
    # implementing logic for accessing columns
    board[j][i]
  end
end

And a quick sanity check:

a = [
%w(O O O O O),
%w(O O O O O),
%w(O O O O O),
%w(O O O O O),
%w(O O O O O)
]

b = [
%w(O O O O O),
%w(O O O O O),
%w(X X X X X),
%w(O O O O O),
%w(O O O O O)
]

c = [
%w(O O O X O),
%w(O O O X O),
%w(O O O X O),
%w(O O O X O),
%w(O O O X O)
]

check_across(a) #=> false
check_across(b) #=> true
check_across(c) #=> false

check_down(a) #=> false
check_down(b) #=> false
check_down(c) #=> true

Upvotes: 2

Related Questions