Class: MyFirstMarkov::Chain

Inherits:
Object
  • Object
show all
Defined in:
lib/my_first_markov/chain.rb

Constant Summary collapse

DEFAULT_COUNT =
5
DEFAULT_DEBUG =
true

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(ordered_entries, debug = DEFAULT_DEBUG) ⇒ Chain

Returns a new instance of Chain.



68
69
70
71
72
73
74
75
# File 'lib/my_first_markov/chain.rb', line 68

def initialize(ordered_entries, debug=DEFAULT_DEBUG)
  @debug = debug
  @entries = Hash.new
  ordered_entries.each_with_index do |entry, index|
    next_entry_idx = next_idx_or_nil(index, ordered_entries.size)
    add(entry, ordered_entries[next_entry_idx]) if next_entry_idx
  end
end

Class Method Details

.default_next_methodObject



14
15
16
# File 'lib/my_first_markov/chain.rb', line 14

def self.default_next_method
  next_methods.first
end

.default_split_on_valueObject



22
23
24
# File 'lib/my_first_markov/chain.rb', line 22

def self.default_split_on_value
  split_on_values.first
end

.file_to_entries(file, split_on, starting_entry, next_method) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/my_first_markov/chain.rb', line 35

def self.file_to_entries(file, split_on, starting_entry, next_method)
  unless split_on && MyFirstMarkov::Chain.split_on_values.include?(split_on.downcase)
    split_on = MyFirstMarkov::Chain.default_split_on_value
  end

  if next_method
    if matches = next_method.match(/^(\D+)(\d+)$/)
      next_method = matches[1]
      count = matches[2]
      unless MyFirstMarkov::Chain.next_methods.include?(next_method.downcase)
        next_method = MyFirstMarkov::Chain.default_next_method
      end
    end
  else
    next_method = MyFirstMarkov::Chain.default_next_method
  end

  unless File.exists?(file)
    fail("Unknown file: #{file.inspect}")
  end

  data = File.read(file)
  ("word" == split_on.downcase) ? entries = data.split : entries = data.split(//)
  entries ||= []

  #puts "return [#{entries.inspect}, #{starting_entry.inspect}, #{next_method.inspect}, #{count || DEFAULT_COUNT}]"
  return [entries, starting_entry, next_method, count || DEFAULT_COUNT]
end

.from_downcase_file(file, split_on, starting_entry, next_method) ⇒ Object



26
27
28
29
# File 'lib/my_first_markov/chain.rb', line 26

def self.from_downcase_file(file, split_on, starting_entry, next_method)
  entries, starting_entry, next_method, count = file_to_entries(file, split_on, starting_entry, next_method)
  return from_entries(entries.map(&:downcase), starting_entry, next_method, count)
end

.from_entries(entries, starting_entry, next_method, count) ⇒ Object



64
65
66
# File 'lib/my_first_markov/chain.rb', line 64

def self.from_entries(entries, starting_entry, next_method, count)
  new(entries).send(next_method.downcase, starting_entry, count)
end

.from_file(file, split_on, starting_entry, next_method) ⇒ Object



31
32
33
# File 'lib/my_first_markov/chain.rb', line 31

def self.from_file(file, split_on, starting_entry, next_method)
  from_entries(*file_to_entries(file, split_on, starting_entry, next_method))
end

.next_methodsObject



10
11
12
# File 'lib/my_first_markov/chain.rb', line 10

def self.next_methods
  ["random_next", "most_likely_next", "first"]
end

.split_on_valuesObject



18
19
20
# File 'lib/my_first_markov/chain.rb', line 18

def self.split_on_values
  ["word", "character"]
end

Instance Method Details

#add(entry, next_entry) ⇒ Object



77
78
79
80
# File 'lib/my_first_markov/chain.rb', line 77

def add(entry, next_entry)
  @entries[entry] ||= Hash.new(0)
  @entries[entry][next_entry] += 1
end

#first(count = nil) ⇒ Object



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/my_first_markov/chain.rb', line 82

def first(count=nil)
  count ||= DEFAULT_COUNT
  # @entries.keys.sort {|a,b| num_observations_for(b) <=> num_observations_for(a) }.take(count)
  results = @entries.keys.reduce({}) { |memo, key|
    memo[key] = num_observations_for(key); memo
  }.sort { |a,b| num_observations_for(b.first) <=> num_observations_for(a.first) }
    .take(count.to_i)

  if (@debug)
    results.reduce({}) { |memo, ary| memo[ary.first] = ary.last; memo }.to_json
  else
    #results.first
    results.map(&:first).to_json # the "entry" part, not the "num_observations"
  end
end

#most_likely_next(entry, count = nil) ⇒ Object



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/my_first_markov/chain.rb', line 98

def most_likely_next(entry, count=nil)
  count ||= DEFAULT_COUNT
  _next(entry) do |observation_total, next_entries_and_observations|
    results = next_entries_and_observations
      .sort {|a,b| b.last <=> a.last} # sort (in reverse) by observations
      .take(count.to_i) # choose the array(s) with the largest observation (could be many with same #)

    if (@debug)
      # debug:
      results.reduce({}) { |memo, ary| memo[ary.first] = ary.last; memo }.to_json
    else
      results.map(&:first).to_json # the "entry" part, not the "num_observations"
    end
  end
end

#random_next(entry, count = nil) ⇒ Object



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/my_first_markov/chain.rb', line 114

def random_next(entry, count=nil)
  count ||= 1
  #puts "called w/ entry: #{entry.inspect}, count: #{count.inspect}"
  _next(entry) do |observation_total, next_entries_and_observations|
    random_threshold = rand(observation_total) + 1
    partial_observation_sum = 0

    results = next_entries_and_observations.select { |next_entry, num_observations|
      partial_observation_sum += num_observations
      partial_observation_sum >= random_threshold
    }.take(count.to_i)

    if (@debug)
      # debug:
      #{ result.first => result.last }.to_json
      results.reduce({}) { |memo, ary| memo[ary.first] = ary.last; memo }.to_json
    else
      #result.first # the "entry" part, not the "num_observations"
      results.map(&:first).to_json # the "entry" part, not the "num_observations"
    end
  end
end