Okay, now I've had some time to think about what it means for the code to be "extensible". Offhand I'm thinking it's as in the "open" part of the Open-Closed Principle, which states that a class should be open to extension but closed to modification. That is, you should be able to extend it o do new and different things, but not so much monkey around with the existing functionality and change how that works -- at least in its interactions with the rest of the world. (How it works inside is another story. That's why encapsulation and Information Hiding are such Good Things.)
That got me thinking, just wrap the functionality in a class, arranged such that one could easily override key methods. So, forthwith my class, and a couple of subclasses:
#! /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 B: take "extensible" as meaning "open to doing additional things" # or "open to doing the same things in different ways". In an OO system, # this generally means "more subclassable". Remember the open part of the # open/closed principle: a class should be *open to extension*. class Word_Manager # This could be overridden to have, for instance, a variable # number of subparts; here we assume two. It would also need to # override find_combos, and decide @max_whole_len differently. def initialize min_len, max_len @max_part_len = max_len @min_part_len = min_len @max_whole_len = @min_part_len + @max_part_len @long_words = [] @short_word_lists = [] (@min_part_len .. @max_part_len).each do |len| @short_word_lists[len] = {} end end def append word len = word.length if len == @max_whole_len then @long_words << word elsif @min_part_len <= len and len <= @max_part_len @short_word_lists[len][word] = nil end end def find_combos hits = [] # hmmm, i'm thinking there may be some way to iterate over # the long words generically, passing in a block.... @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_whole_len - 1] if has_word? part_1 and has_word? part_2 hits << "#{part_1} + #{part_2} => #{word}" end end end hits end def has_word? word len = word.length # note: generally for parts, not wholes if len == @max_whole_len then @long_words.include? word elsif @min_part_len <= len and len <= @max_part_len @short_word_lists[len].has_key? word else false end end def read_file file_name # a subclass could insert code to unify case, or translate, or . . . . File.new(file_name, 'r').each_line { |line| append line.chomp } end end # Now let's make a couple subclasses. # one that's not case-sensitive class Caseless_Word_Manager < Word_Manager def append word super word.downcase end end # one that will consider a part to be OK both forward and backward; # only makes sense if also caseless class Reversible_Word_Manager < Word_Manager def append word down = word.downcase super down # could instead override has_word to check for reversed version; # which one makes sense depends on frequency of insert vs. check super down.reverse if down.length < @max_whole_len end end # Now let's see what each one does with our word list! def helper obj, description obj.read_file 'conflict_words.txt' hits = obj.find_combos puts "#{description} hits:\n#{hits.join "\n"}\n#{hits.length} hits" end helper Word_Manager.new(1, 5), 'Normal' puts helper Caseless_Word_Manager.new(1, 5), 'Caseless' puts helper Reversible_Word_Manager.new(1, 5), 'Reversible'
And while I'm at it... here is the list of words I've been using, so you can test the code yourself without waiting while it chugs through your system wordlist:
A AT ATTACK Reb ate atwirl be bed bedpan bemoan benighted bie big bigger boo booger but conic credible days debbie ed er ers ger German i iconic in incredible inside insight intake irides istoke land lander man moan morrow night on pan rebate rides rub rubber side sight stoke tack tackon take takeon to todays tomorrow tubers twirl
So now, as always, it's your turn. What does "extensible" mean to you? How would you have made it so?