Class: RubyLabs::ElizaLab::Pattern

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

Overview

Pattern

A Pattern represents one way to transform an input sentence into a response. A Pattern instance has a regular expression and a list of one or more reassembly strings that can refer to groups in the expression. There is also an index to record the last reassembly string used, so the application can cycle through the strings.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(expr, list = []) ⇒ Pattern

Create a new sentence pattern that will apply to input sentences that match expr. The argument can be either a string or a regular expression. If the argument is a string, it is converted to a regular expression that matches exactly that string, e.g. “duck” is converted to /duck/.

To make it easier for uses to create patterns without knowing too many details of regular expressions the constructor modifies the regular expression:

word breaks

Insert word break anchors before the first word and after the last word in the expression

case insensitive

Add a i modifier to the regular expression

wildcards

Insert parentheses around “.*”

variables

Insert parentheses around variable names of the form “$n”

alternatives

Insert parentheses around groups of words, e.g. “a|b|c”

To see the real final regular expression stored with a rule call the regexp accessor method.

Example:

>> p = Pattern.new("duck")
=> duck: []
>> p.regexp
=> /\bduck\b/i

>> p = Pattern.new("plane|train|automobile")
=> (plane|train|automobile): []
>> p.regexp
=> /(plane|train|automobile)/i

>> p = Pattern.new("I don't like .*")
=> I don't like (.*): []
>> p.regexp
=> /\bI don't like (.*)/i

– Pattern.new called internally only from Rule#addPattern, which is called to add /.*/ for default rule, or when reading /…/ line from script.

In interactive experiments, users can call Pattern.new(s) or Pattern.new(s,a) where s is a string or regexp, and a is an array of response strings.



185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/elizalab.rb', line 185

def initialize(expr, list = [])
  raise "Pattern#initialize: expr must be String or Regexp" unless (expr.class == String || expr.class == Regexp)
  re = (expr.class == String) ? expr : expr.source
  add_parens(re, /\(?\.\*\)?/ )
  add_parens(re, /\(?[\w' ]+(\|[\w' ]+)+\)?/ )
  add_parens(re, /\(?\$\w+\)?/ )
  re.insert(0,'\b') if re =~ /^\w/
  re.insert(-1,'\b') if re =~ /\w$/
  @regexp = Regexp.new(re, :IGNORECASE)
  @list = list.nil? ? Array.new : list
  @index = 0    
end

Instance Attribute Details

#indexObject

Returns the value of attribute index.



145
146
147
# File 'lib/elizalab.rb', line 145

def index
  @index
end

#listObject

Returns the value of attribute list.



145
146
147
# File 'lib/elizalab.rb', line 145

def list
  @list
end

#mdObject

Returns the value of attribute md.



145
146
147
# File 'lib/elizalab.rb', line 145

def md
  @md
end

#regexpObject

Returns the value of attribute regexp.



145
146
147
# File 'lib/elizalab.rb', line 145

def regexp
  @regexp
end

Instance Method Details

#add_parens(s, r) ⇒ Object

Helper method called by the constructor – add parentheses around every occurrence of the string r in sentence pattern s. Checks to make sure there aren’t already parentheses there.



209
210
211
# File 'lib/elizalab.rb', line 209

def add_parens(s, r)
  s.gsub!(r) { |m| ( m[0] == ?( ) ? m : "(" + m + ")" }
end

#add_response(s) ⇒ Object

Add sentence s to the set of response strings for this pattern.



215
216
217
# File 'lib/elizalab.rb', line 215

def add_response(s)
  @list << s
end

#apply(s, opt = :preprocess) ⇒ Object

Try to apply this pattern to input sentence s. If s matches the regular expression for this rule, extract the parts that match groups, insert them into the next response string, and return the result. If s does not match the regular expression return nil.

The second argument should be a symbol that controls whether or not the method applies preprocessing rules. The default is to apply preprocessing, which is the typical case when users call the method from an IRB session. But when Eliza is running, preprocessing is done already, so this argument is set to :no_preprocess.



229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/elizalab.rb', line 229

def apply(s, opt = :preprocess)
  Eliza.preprocess(s) if opt == :preprocess
  @md = s.match(@regexp)
  return nil if @list.empty? || @md == nil
  res = @list[inc()].clone
  return res if res[0] == ?@
  puts "reassembling '#{res}'" if @@verbose
  res.gsub!(/\$\d+/) do |ns|
    n = ns.slice(1..-1).to_i        # strip leading $, convert to int
    if n && @md[n]
      puts "postprocess #{@md[n]}" if @@verbose
      @md[n].gsub(/[a-z\-$']+/i) do |w| 
        (@@post.has_key?(w) && @@post[w][0] != ?$) ? @@post[w] : w
      end
    else
      warn "Pattern.apply: no match for #{ns} in '#{res}'"
      ""
    end
  end
  return res
end

#cleanRegexpObject

Helper method called by inspect and to_s – remove the word boundary anchors from the regular expression so it is easier to read.



284
285
286
287
288
# File 'lib/elizalab.rb', line 284

def cleanRegexp
  res = @regexp.source
  res.gsub!(/\\b/,"")
  return res
end

#inspectObject

Create a more detailed string summarizing the pattern and its possible responses.



277
278
279
# File 'lib/elizalab.rb', line 277

def inspect
  return cleanRegexp + ": " + @list.inspect 
end

#match(s) ⇒ Object

Helper method – return true if sentence s matches the regular expression for this pattern.



254
255
256
257
# File 'lib/elizalab.rb', line 254

def match(s)
  @md = s.match(@regexp) 
  return @md != nil
end

#partsObject

Helper method – return an array of parts of the input sentence captured when the input was compared to the regular expression and that matched any wild cards or groups in the regular expression.



263
264
265
# File 'lib/elizalab.rb', line 263

def parts
  return @md.nil? ? nil : @md.captures
end

#resetObject

Reset the internal counter in this pattern, so that the next response comes from the first response string.



201
202
203
# File 'lib/elizalab.rb', line 201

def reset
  @index = 0
end

#to_sObject

Create a string that summarizes the attributes of this pattern.



269
270
271
272
273
# File 'lib/elizalab.rb', line 269

def to_s
  s = "  /" + cleanRegexp + "/\n"
  @list.each { |x| s += "    \"" + x + "\"\n" }
  return s
end