This isn't much to look at, but it appears to be > 48 hours since the
posting. So I thought I'd post it anyway. I still can't generate
the example pangrams given at
http://www.cs.indiana.edu/~tanaka/GEB/
pangram.txt but it does generate this pangram in 1-5 minutes:
This terribly inefficient pangram contains five a's, two b's, three
c's, two d's, thirty-one e's, six f's, four g's, ten h's, sixteen
i's, one j, one k, three l's, two m's, twenty n's, thirteen o's, two
p's, one q, twelve r's, twenty-eight s's, twenty-eight t's, three
u's, three v's, nine w's, four x's, six y's and one z.
It's a basic randomized robinsoniziz..ing. With a rather absurd
amount of test code.
-Mat
-- pangram.rb
#!/usr/bin/ruby
# Ruby Quiz 86, Pangrams: Solution by Mat Schaffer <
[email protected]>
# uses numeric_spell library available from
http://tanjero.com/svn/
plugins/numeric_spell/
require 'numeric_spell'
class SelfDocumentingPangram
LETTERS = (?a..?z).to_a
def initialize(starter_string = "This test starter contains ")
@start = starter_string
end
def to_s
current = count(
@start)
actual = count(add_count(current))
while current != actual
LETTERS.each do |letter|
current[letter] = rand_between(current[letter], actual[letter])
end
actual = count(add_count(current))
end
add_count(current)
end
def rand_between a,b
range = (a - b).abs + 1
rand(range) + [a,b].min
end
def add_count(counts)
@start + counts_to_s(counts)
end
def count_to_s(char, count)
if count != 1
count.spell + " " + char.chr + "'s"
else
count.spell + " " + char.chr
end
end
def counts_to_s(count)
string_counts = []
LETTERS.each do |letter|
string_counts << count_to_s(letter, count[letter])
end
last = string_counts.pop
string_counts.join(", ") + " and " + last + "."
end
def count(string)
count = Hash.new(0)
string.downcase.each_byte do |letter|
if LETTERS.include? letter
count[letter] += 1
end
end
count
end
end
if ARGV[0] =~ /test/i
require 'test/unit'
class TestSelfDocumentingPangram < Test::Unit::TestCase
# checks that count will yield accurate counts for only letters,
ignoring case
def test_count
# check basic case containing only a..z
string = ('a'..'z').to_a.to_s
count = SelfDocumentingPangram.new.count(string)
assert_equal(26, count.length)
count.each do |key, value|
assert_equal(1, value)
end
# check case for a..z, A..Z, and some punctiation that we're
likely to use
string = (('a'..'z').to_a + ('A'..'Z').to_a + ['\'', ',', '.',
'-']).to_s
count = SelfDocumentingPangram.new.count(string)
assert_equal(26, count.length)
count.each do |key, value|
assert_equal(2, value)
end
end
def test_count_to_s
assert_equal("one a", SelfDocumentingPangram.new.count_to_s(?
a, 1))
assert_equal("fifteen z's",
SelfDocumentingPangram.new.count_to_s(?z, 15))
assert_equal("forty-two d's",
SelfDocumentingPangram.new.count_to_s(?d, 42))
end
def test_counts_to_s
start = "The last of these contained "
expected = "two a's, zero b's, one c, one d, four e's, one f,
zero g's, two h's, one i, zero j's, zero k's, one l, zero m's, two
n's, two o's, zero p's, zero q's, zero r's, two s's, four t's, zero
u's, zero v's, zero w's, zero x's, zero y's and zero z's."
pangram = SelfDocumentingPangram.new
result = pangram.counts_to_s(pangram.count(start))
assert_equal(expected, result)
end
def test_rand_between
100.times do
a = rand(100)
b = [a, rand(100)].max
c = SelfDocumentingPangram.new.rand_between(a,b)
assert (a..b) === c, "#{c} is not between #{a} and #{b}"
end
end
def test_add_count
pangram = SelfDocumentingPangram.new("hi ")
count = Hash.new(0)
expected = "hi " + pangram.counts_to_s(Hash.new(0))
assert_equal(expected, pangram.add_count(Hash.new(0)))
end
# runs the SelfDocumentingPangram class to verify that it can
produce the pangrams found at
#
http://www.cs.indiana.edu/~tanaka/GEB/pangram.txt
def test_to_s
pangram1 = "This pangram tallies five a's, one b, one c, two
d's, twenty-eight e's, eight f's, six g's, eight h's, thirteen i's,
one j, one k, three l's, two m's, eighteen n's, fifteen o's, two p's,
one q, seven r's, twenty-five s's, twenty-two t's, four u's, four
v's, nine w's, two x's, four y's and one z."
assert_equal(pangram1, SelfDocumentingPangram.new("This
pangram tallies ").to_s)
#pangram2 = "This computer-generated pangram contains six a's,
one b, three c's, three d's, thirty-seven e's, six f's, three g's,
nine h's, twelve i's, one j, one k, two l's, three m's, twenty-two
n's, thirteen o's, three p's, one q, fourteen r's, twenty-nine s's,
twenty-four t's, five u's, six v's, seven w's, four x's, five y's and
one z."
#assert_equal(pantram2, SelfDocumentingPangram.new("This
computer-generated pangram contains ").to_s)
end
# This is mainly a sanity check to see that a pangram will
evaluate to itself when counted and regenerated
def test_approach
prefix = "This pangram tallies "
solution = "This pangram tallies five a's, one b, one c, two
d's, twenty-eight e's, eight f's, six g's, eight h's, thirteen i's,
one j, one k, three l's, two m's, eighteen n's, fifteen o's, two p's,
one q, seven r's, twenty-five s's, twenty-two t's, four u's, four
v's, nine w's, two x's, four y's and one z."
pangram = SelfDocumentingPangram.new(prefix)
assert_equal(solution, pangram.add_count(pangram.count
(solution)))
prefix = "This terribly inefficient pangram contains "
solution = "This terribly inefficient pangram contains five
a's, two b's, three c's, two d's, thirty-one e's, six f's, four g's,
ten h's, sixteen i's, one j, one k, three l's, two m's, twenty n's,
thirteen o's, two p's, one q, twelve r's, twenty-eight s's, twenty-
eight t's, three u's, three v's, nine w's, four x's, six y's and one z."
pangram = SelfDocumentingPangram.new(prefix)
assert_equal(solution, pangram.add_count(pangram.count
(solution)))
end
end
else
puts SelfDocumentingPangram.new("This terribly inefficient pangram
contains ").to_s
end
The three rules of Ruby Quiz:
1. Please do not post any solutions or spoiler discussion for this
quiz until
48 hours have passed from the time on this message.
2. Support Ruby Quiz by submitting ideas as often as you can:
http://www.rubyquiz.com/
3. Enjoy!
Suggestion: A [QUIZ] in the subject of emails about the problem
helps everyone
on Ruby Talk follow the discussion. Please reply to the original
quiz message,
if you can.
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
=-=-=-=-=-=-=
by Darren Kirby
One thing that interests me are word puzzles and language oddities.
One such
example is the self-documenting panagram. If a panagram is a
sentence that uses
every letter in the alphabet, then a self-documenting panagram is a
sentence
that enumerates its own letter count. Simple enough, but what if we
state that
the letter count must be spelled ie: 'twenty-seven' instead of
'27'. Now we
have a challenge.
A while back I wrote a script in Python that finds these sentences.
Today I
rewrote it in Ruby and it found me this sentence:
Darren's ruby panagram program found this sentence which contains
exactly
nine 'a's, two 'b's, five 'c's, four 'd's, thirty-five 'e's, nine
'f's,
three 'g's, nine 'h's, sixteen 'i's, one 'j', one 'k', two 'l's,
three 'm's,
twenty-seven 'n's, fourteen 'o's, three 'p's, one 'q', fifteen 'r's,
thirty-four 's's, twenty-two 't's, six 'u's, six 'v's, seven 'w's,
six 'x's,
seven 'y's, and one 'z'.
My script does have its problems, and I would love to see what kind
of code the
Ruby experts could come up with to find self-documenting panagrams.
There is a lot more info on self-documenting panagrams at this
address:
http://www.cs.indiana.edu/~tanaka/GEB/pangram.txt