I\'m working on a Ruby verison of RSG and somehow stuck on the sentence generating process (...)
so I managed to implement all functions like read, convert to hash...,et
I assume they're looking for a recursive method, let's call it generate
.
def generate(key)
Read the hash at the key and take one randomly using sample
:
words = @hash[key].sample
Then, for each word, check to see if it's a <key>
. If so, call generate
on it, otherwise save it:
if (word.start_with?("<") && word.end_with?(">"))
generate(word)
else
@sentence << word
end
Putting it all together:
@hash = {"<start>"=>[["The", "<object>", "<verb>", "tonight."]],
"<object>"=>[["waves"], ["big", "yellow", "flowers"], ["slugs"]],
"<verb>"=>[["sigh", "<adverb>"], ["portend", "like", "<object>"],["die", "<adverb>"]],
"<adverb>"=>[["warily"], ["grumpily"]]}
@sentence = []
def generate(key)
words = @hash[key].sample
words.each do |word|
if (word.start_with?("<") && word.end_with?(">"))
generate(word)
else
@sentence << word
end
end
end
generate("<start>")
puts @sentence.join(" ")
Notice I used @-variables to make their scope reachable from within the method.
Sample output: The big yellow flowers sigh grumpily tonight.
Code
def generate(hash, start_key)
mod_hash = hash.transform_values{ |v| v.map { |a| a.join(' ') } }
sentence = mod_hash[start_key].sample
while sentence.include?('<')
sentence.gsub!(/\<.+?\>/) { |s| mod_hash[s].sample }
end
sentence
end
Examples
hash = { "<start>" =>[["The", "<object>", "<verb>", "tonight."]],
"<object>"=>[["waves"], ["big", "yellow", "flowers"], ["slugs"]],
"<verb>" =>[["sigh", "<adverb>"], ["portend", "like", "<object>"],
["die", "<adverb>"]],
"<adverb>"=>[["warily"], ["grumpily"]]}
generate(hash, '<start>') #=> "The big yellow flowers die grumpily tonight."
generate(hash, '<start>') #=> "The waves die warily tonight."
generate(hash, '<start>') #=> "The slugs sigh warily tonight."
generate(hash, '<verb>') #=> "portend like big yellow flowers"
Explanation
Firstly, mod_hash
is constructed.
mod_hash = hash.transform_values{ |v| v.map { |a| a.join(' ') } }
#=> {"<start>" =>["The <object> <verb> tonight."],
# "<object>"=>["waves", "big yellow flowers", "slugs"],
# "<verb>" =>["sigh <adverb>", "portend like <object>", "die <adverb>"],
# "<adverb>"=>["warily", "grumpily"]}
Then the initial sentence is obtained.
start_key = '<start>'
sentence = mod_hash[start_key].sample
#=> "The <object> <verb> tonight."
We now simply replace each word in sentence
that begins '<'
and ends '>'
with a randomly-selected element of the value of that key in mod_hash
(the value being an array of strings). This continues until there are no more such words in sentence
.
The question mark in the regex means that one or more characters are to be matched lazily. That means that the match is terminated as soon as the first '>'
is encountered. If, for example, the sentence were "a <hat> and <cat>!"
, the regex would match both <hat>
and <cat>
. By contrast, if the match were greedy (the default), it would match "<hat> and <cat>"
, which of course is not a key of mod_hash
.
Note that hash
could have a structure that results in a non-terminating sequence of replacements.
See Hash#transform_values.