Re: Markov Chain (#74)

Author: Shane E.

Quiz 74: Markov Chains

This is a bug fix and enhancement to my previous solution.

Originally this was a recreation of the Mark V. Shaney program.

However, after extensive work it is something new. You can now

choose how many words can be stored as a phrase (the default

is 2). I have also stopped storing Hash keys in reverse order

since it was hindering debugging. In doing this, I found other

problems in the selection of words which is now fixed. If you

see anything that is to wordy, wrong, or can be Rubified let

me know.

I was thinking, but have not tried, what would happen if you

sent source code through the algorithm? Obviously some things

would need to be changed for “phrase_breaks”, but I wonder if

anything would actually sucessfully run.

class MarkovChain

def initialize( book, max_phrase_size = 2 )
    @book = book
    @phrases = Array.new( max_phrase_size )
    @phrases.each_index { |i| @phrases[ i ] = Hash.new }
    @phrase_breaks = Array.new
end

def read( book = @book )
    prev = Array.new( @phrases.length ).fill( '' )
    words = File.open( book ).read.split
    words.each do |word|
        word.gsub!( /["()]/, '' )
        unless prev[ -1 ].eql?( '' )
            @phrases.each_index do |i|
                prev_words = prev[ @phrases.length - i .. 

prev.length

  • 1 ].join( ‘’ )
    @phrases[ i ][ prev_words ] = Array.new unless
    @phrases[ i ].has_key?( prev_words )
    @phrases[ i ][ prev_words ] << word.downcase
    @phrase_breaks << prev_words if prev_words.match(
    /[.!?]$/ )
    end
    end
    prev.shift and prev.push( word )
    end
    end

    def get_chain( num_want = 5 )
    chain = Array.new
    num_made = 0
    prev = Array.new( @phrases.length ).fill( ‘’ )
    prevs = Array.new( @phrases.length )
    prev.each_index do |i|
    prevs[ i ] = prev[ @phrases.length - i … prev.length - 1
    ].join( ‘’ )
    end
    while num_made < num_want do
    found = false
    @phrases.each_index do |i|
    found = true if @phrases[ i ].has_key?( prevs[ i ] )
    end
    until found
    prev = @phrase_breaks[ rand( @phrase_breaks.length )
    ].split
    prev << ‘’ until prev.length == @phrases.length
    prev.each_index do |i|
    prevs[ i ] = prev[ @phrases.length - i …
    prev.length

  • 1 ].join( ‘’ )
    end
    @phrases.each_index do |i|
    found = true if @phrases[ i ].has_key?( prevs[ i ] )
    end
    end
    words = Array.new
    @phrases.each_index do |i|
    prev_words = prev[ @phrases.length - i … prev.length -
    1
    ].join( ‘’ )
    words = @phrases[ i ][ prev_words ] if
    @phrases[ i ].has_key?( prev_words )
    end
    word = words[ rand( words.length ) ]
    chain << word
    prev.shift and prev.push( word )
    prev.each_index do |i|
    prevs[ i ] = prev[ @phrases.length - i … prev.length -
    1
    ].join( ‘’ )
    end
    num_made += 1 if word.match( /[.!?]$/ )
    end
    chain
    end

end

mChain = MarkovChain.new( ARGV[ 0 ] )
mChain.read
print mChain.get_chain.join( ’ ’ ), “\n”