Ok, for a change of pace let’s break away from sorting algorithms and do this problem:
Printing a Multiplication Table of the First 10 Prime Numbers
I got this as a coding challenge a few days ago and finally got a chance to tackle it this weekend. The prompt asks for the user to create a program that runs on the command line, and it should print out a multiplication table to STDOUT. I feel like I’ve seen similar problems elsewhere, but this one had the twist that it wanted the multiplier numbers to be primes.
Other rules: Make it flexible for N # primes, write some tests, don’t use Ruby’s Prime class, package yer code.
I started out this challenge by trying to understand clearly what the problem was. Print out a multiplication table? Like this?
Oooooooooooooh. Haven’t seen one of those in a long time.
So the top row and the first column would be populated with primes. Hmm…
Since I couldn’t use Ruby’s Prime class from the standard library, writing a method for finding primes seemed like the first thing to do.
def is_prime?(num) | |
divisors_array = [] | |
(1..num).each do |n| | |
if num % n == 0 | |
divisors_array << num | |
end | |
end | |
divisors_array.length > 2 ? false:true | |
end |
I wanted to make my program flexible so you could create multiplication tables of different sizes. So I created a method to pull the first n primes into an array.
def pull_primes(num) | |
primes_array = [] | |
counter = 2 | |
until primes_array.size == num | |
if is_prime?(counter) | |
primes_array << counter | |
end | |
counter += 1 | |
end | |
primes_array | |
end |
And finally actually printing out the actual table! Formatting was the biggest pain here.
def print_table(num) | |
rows = pull_primes(num) #array | |
columns = rows | |
print " " | |
columns.each {|column_num| print " %-3d " % column_num} | |
print "\n\n" | |
rows.each do |row_num| | |
print "%-3d| " % row_num | |
columns.each {|column_num| print " %-3d " % (column_num * row_num)} | |
print "\n\n" | |
end |
After I was done with these methods, I thought I was good to go. HA! Nope, still got to package it up and write some tests. I was going back and forth, but I’ll talk about the tests first.
I decided to go with RSpec, so I created a gemfile and added in the requirement. Then I created some spec docs and started going to town. I really only had 3 methods to test, and I decided to do 2 tests for each. It’s been a while since I watched ThoughtBot’s TDD lecture, and my subscription to their site has lapsed, so I decided to keep it as simple as possible. An RSpec tutorial I was reading was referencing a “Calculator” class in its tests, so I thought “Oh, I might as well bundle up the 3 methods into a MultiplicationTable class.” So I went back and changed that, and my tests.
All of the tests were pretty simple except for the print_tables one. Again, it all came down to formatting. I wasn’t sure how to check what was being printed to STDOUT. I came across stringio and some helper methods on StackOverflow, threw them into my spec_helper, and then it was on. I could capture and check what was being printed to the console. Still, it was tricky to figure out what string I was looking for here. RSpec failure messages came to the rescue as I just copied the extremely ugly failure string (making sure #’s were correct) and pasted into my tests.
describe MultiplicationTable do | |
before do | |
@multiplication_table = MultiplicationTable.new | |
end | |
describe "#is_prime?" do | |
context 'testing the number 5' do | |
it "returns true" do | |
expect(@multiplication_table.is_prime?(5)).to eql(true) | |
end | |
end | |
context 'testing the number 10' do | |
it "returns false" do | |
expect(@multiplication_table.is_prime?(10)).to eql(false) | |
end | |
end | |
end | |
describe "#pull_primes" do | |
context 'testing with num = 10' do | |
it 'size is 10' do | |
expect(@multiplication_table.pull_primes(10).size == 10) | |
end | |
end | |
context 'testing with num = 2' do | |
it 'size is 2' do | |
expect(@multiplication_table.pull_primes(2).size == 2) | |
end | |
end | |
end | |
describe "#print_table" do | |
context 'testing with num = 4' do | |
it 'prints a 4 x 4 table' do | |
printed = capture_stdout do | |
@multiplication_table.print_table(4) | |
end | |
printed.should eq(" 2 3 5 7 \n\n2 | 4 6 10 14 \n\n3 | 6 9 15 21 \n\n5 | 10 15 25 35 \n\n7 | 14 21 35 49 \n\n") | |
end | |
end | |
context 'testing with num = 2' do | |
it 'prints a 2 x 2 table' do | |
printed = capture_stdout do | |
@multiplication_table.print_table(2) | |
end | |
printed.should eq(" 2 3 \n\n2 | 4 6 \n\n3 | 6 9 \n\n") | |
end | |
end | |
end | |
end |
Hooray! My tests were looking for the right things now. Nevermind that when I ran the tests I got this little deprecation warning:
Um… ok. Sure I can go back and change that easily. Nope, getting errors after I tried to fix it. I’ll go back and deal with it later.
I started out thinking about how to package my program. My mind wandered to gems, which of course made me think about my NPR Stories Gem. I looked at my gem to check out the basic structure, see what I was still missing in my multiplication table project. Then I got sidetracked for about 30 minutes trying to push my NPR gem to RubyGems. I hadn’t yet actually published my NPR gem for a variety of different failures, but this time I realized one major reason- I had to actually build the gem first with:
gem build npr_stories.gemspec
then I could push it to RubyGems. D’oh. That was embarrassing. Now that it was published, and robots were already scraping it, I wanted to make sure that it worked, so I spent some more time trying to edit and test my NPR gem, then I remembered that I had to finish this coding challenge. Right. And of course there was a typo in my gemspec file. Not worth doing a whole gem update for a single letter typo.
Ok, back to multiplication tables! So I could package this multiplication table as a gem for easy installation…But is it even worth it? If so, I should have set this up as a gem to begin with so I could have used this bundler command that pulls together all those files and folder structure for you:
$ bundle gem my_gem
I decided to do it anyways and created a new repo to play with. I just copied over my tiny amount of code thus far.
15 minutes later I abandoned ship and returned to my original repo. Creating a gem just didn’t make sense to me for something so small. Hmmmm… how did I want this to run on the command line? I created a bin folder & executable, and also a CLI class with a simple interface.
#!/usr/bin/env ruby | |
require "bundler/setup" | |
require "./lib/multiplication_table" | |
require "./lib/cli" | |
CLI.new.call |
class CLI | |
def call | |
choice = nil | |
until choice == 'N' | |
puts "Let's Multiply!!!" | |
@multiplication_table = MultiplicationTable.new | |
puts "Enter an integer for table size" | |
table_size = gets.strip.to_i | |
@multiplication_table.print_table(table_size) | |
puts "Run again? Y or N?" | |
choice = gets.strip.upcase | |
end | |
puts "Now exiting. Thanks!" | |
end | |
end |
And after a few hiccups it’s working!
The last step I had was to write a README doc. Whew! That was a lot of work for such a simple little ask. It would have gone faster if I hadn’t indulged my little diversions to other projects and down multiple rabbit holes.