Morton Goldberg
8/5/2007 3:56:00 PM
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