Sunday, August 22, 2010

Kata 8: Conflicting Objectives, Part 3A

   Part 3, much like Part 1, is open to much interpretation.  I figure that the sense of "extensible" that Dave Thomas probably meant to drive at, was "easy to add features to", most likely by making subclassing easy, and accepting code blocks, and things like that.  I've been giving that some thought, and it makes my head hurt. :-)

   So for now, I'm going to "creatively misinterpret" it as "flexible" and "easy to put to many different uses by accepting a number of different ways it can do the job".  Here is my extensible flexible version:

#! /usr/bin/ruby

# Dave Thomas Code Kata #8: Conflicting Objectives
# See http://codekata.pragprog.com/2007/01/kata_eight_conf.html
# Solution by Dave Aronson
# version 3: make it extensible
# part A: creatively misinterpret "extensible" as "flexible" :-)


# IDEA to make it more extensible, tho MUCH slower:
# ability to split into larger # of parts?
# could mitigate by also allowing minimum part length.


def make_word_lists file_name, case_sensitive, max_part_len, min_part_len
  long_words = []
  short_word_lists = []

  (min_part_len .. max_part_len).each { |len| short_word_lists[len] = {} }

  max_len = min_part_len + max_part_len

  File.new(file_name, 'r').each_line do |line|
    word = line.chomp
    len = word.length
    word.downcase! if not case_sensitive
    if len == max_len then long_words << word
    elsif min_part_len <= len and len <= max_part_len
      short_word_lists[len][word] = nil
    end
  end
  return [long_words, short_word_lists]
end


def find_combos long_words, short_word_lists, max_part_len, min_part_len
  hits = []
  max_len = min_part_len + max_part_len
  long_words.each do |word|
    (min_part_len .. max_part_len).each do |len|
      part_1 = word[0 .. len - 1]
      part_2 = word[len .. max_len - 1]
      if short_word_lists[len].has_key? part_1 and
         short_word_lists[max_len-len].has_key? part_2
        hits << "#{part_1} + #{part_2} => #{word}"
      end
    end
  end
  hits
end


file_name = 'conflict_words.txt'
max_len = 6
max_part_len = 5
min_part_len = 1
case_sensitive = false

# I'll skip all the overhead of getting and parsing the above as args....

start_time = Time.now
long_words, short_word_lists = make_word_lists file_name, case_sensitive,
                                               max_part_len, min_part_len

hits = find_combos long_words, short_word_lists, max_part_len, min_part_len
puts "hits:\n#{hits.join "\n"}\n#{hits.length} hits"
puts "Running time: #{Time.now - start_time} seconds"

   Okay, now I could use a bit of help from the assorted Rubyists out there.  Where do you see places I could install hooks on which people can hang code blocks?  What do you think would ease subclassing to change the behavior?  Or do you think that's the wrong approach entirely, that I should separate the workings out, as in the Strategy or Visitor patterns?  I've got too much more important stuff to do today, to give this the thought it deserves right now.