Here are some solutions to this quiz. The first solution deliberately  
avoids using regular expressions. Note the use of next to skip over  
words that are too short or capitalized and break to stop the  
iteration when it gets into territory beyond where numbers of the  
given base exist.

<code>
WORD_LIST = "/usr/share/dict/words"
WORDS = File.read(WORD_LIST).split

def number_words(base=16, min_letters=3)
    result = []
    WORDS.each do |w|
       next if w.size < min_letters || (?A..?Z).include?(w[0])
       break if w[0] > ?a + (base - 11)
       result << w if w.to_i(base).to_s(base) == w
    end
    result
end
</code>

<example>
number_words(18, 5) # => ["abaca", "abaff", "accede", "achage",  
"adage", "added", "adead", "aface", "ahead", "bacaba", "bacach",  
"bacca", "baccae", "bache", "badge", "baggage", "bagged", "beach",  
"beached", "beachhead", "beaded", "bebed", "bedad", "bedded",  
"bedead", "bedeaf", "beech", "beedged", "beefhead", "beefheaded",  
"beehead", "beeheaded", "begad", "behead", "behedge", "cabbage",  
"cabbagehead", "cabda", "cache", "cadge", "caeca", "caffa", "caged",  
"chafe", "chaff", "chebec", "cheecha", "dabba", "dagaba", "dagga",  
"dahabeah", "deadhead", "debadge", "decad", "decade", "deedeed",  
"deface", "degged", "dhabb", "echea", "edged", "efface", "egghead",  
"facade", "faced", "faded", "fadge", "feedhead", "gabgab", "gadbee",  
"gadded", "gadge", "gaffe", "gagee", "geggee", "hache", "haggada",  
"hagged", "headache", "headed", "hedge"]
</example>

The second solution uses #inject rather than #each, but doesn't seem  
to be much if any of an improvement. I found it interesting because  
it's one of few times I've ever needed to pass an argument to break  
and next.

<code>
WORD_LIST = "/usr/share/dict/words"
WORDS = File.read(WORD_LIST).split

def number_words(base=16, min_letters=3)
    WORDS.inject([]) do |result, w|
       next result if w.size < min_letters || (?A..?Z).include?(w[0])
       break result if w[0] > ?a + (base - 11)
       result << w if w.to_i(base).to_s(base) == w
       result
    end
end
</code>

<example>
number_words(20, 7) # => ["accidia", "accidie", "acidific",  
"babiche", "bacchiac", "bacchic", "bacchii", "badiaga", "baggage",  
"beached", "beachhead", "beedged", "beefhead", "beefheaded",  
"beehead", "beeheaded", "behedge", "bighead", "cabbage",  
"cabbagehead", "caddice", "caddiced", "caffeic", "cheecha",  
"cicadid", "dahabeah", "deadhead", "debadge", "debeige", "decadic",  
"decafid", "decided", "deedeed", "deicide", "diffide", "edifice",  
"egghead", "feedhead", "giffgaff", "haggada", "haggadic", "headache",  
"jibhead"]
</example>

In my third and last solution, I take the obvious route and use  
regular expressions. Maybe regular expressions are better after all.

<code>
WORD_LIST = "/usr/share/dict/words"
WORDS = File.read(WORD_LIST).split

def number_words(base=16, min_letters=3)
    biggest_digit = (?a + (base - 11))
    regex = /\A[a-#{biggest_digit.chr}]+\z/
    result = []
    WORDS.each do |w|
       next if w.size < min_letters || w =~ /^[A-Z]/
       break if w[0] > biggest_digit
       result << w if w =~ regex
    end
    result
end
</code>

The following are all the hex numbers in word list which have at  
least three letters.

<example>
number_words # => ["aba", "abac", "abaca", "abaff", "abb", "abed",  
"acca", "accede", "ace", "adad", "add", "adda", "added", "ade",  
"adead", "aface", "affa", "baa", "baba", "babe", "bac", "bacaba",  
"bacca", "baccae", "bad", "bade", "bae", "baff", "bead", "beaded",  
"bebed", "bed", "bedad", "bedded", "bedead", "bedeaf", "bee", "beef",  
"cab", "caba", "cabda", "cad", "cade", "caeca", "caffa", "cede",  
"cee", "dab", "dabb", "dabba", "dace", "dad", "dada", "dade", "dae",  
"daff", "dead", "deaf", "deb", "decad", "decade", "dee", "deed",  
"deedeed", "deface", "ebb", "ecad", "edea", "efface", "facade",  
"face", "faced", "fad", "fade", "faded", "fae", "faff", "fed", "fee",  
"feed"]
</example>

Regards, Morton