Later a shorter Ruby one was posted that used regular expressions:
S =
[ /father|mother|brother|sister/i, "Tell me about your 0."],
[ /\b(am|i'm) (.*)/i, ["Why are you 2?","Have you always been 2?"]],
[ /\bI was (.*)/i, ["Why were you 1?","I can't believe you were 1."]],
[ /\bI will (.*)/i, "Do you think it's wise to 1?"],
[ /\bI (.*)/i, "Why do you 1?" ],
[ /\b(you|your|yours)\b/i, ["We're talking about you, not me.",
"Please don't be so personal."]],
[ /.*/, ["That's very interesting. Do go on.",
"Tell me more.",
"I'm not sure that I understand you fully.",
"Can you elaborate on that?" ]]
P = Hash[ *"I|you|my|your|myself|yourself|you are|I am|you're|I am".
split('|')]
( gets;sub(/[.!?,; ]+$/,"");x=Array(S.find{|a|$m=$_.match(a[0])}[1])
puts x[rand(x.size)].gsub(/\d/){$m.to_a[$&.to_i].scan(
/you are|you're|\w+|\W+/).map{|s|P[s]||P.invert[s]||s}.join}
) while 9
By eliminating the step of changing the person of pronouns
in the user's input (e.g., "your" to "my"), a step that the
original Lisp program didn't perform, the Ruby
code was reduced to 2 lines (not counting the "script"):
S =
[ /father|mother|brother|sister/i, "Tell me about your 0."],
[ /\b(am|i'm) (.*)/i, ["Why are you 2?","Have you always been 2?"]],
[ /\bI was (.*)/i, ["Why were you 1?","I can't believe you were 1."]],
[ /\bI will (.*)/i, "Do you think it's wise to 1?"],
[ /\bI (.*)/i, "Why do you 1?" ],
[ /\b(you|your|yours)\b/i, ["We're talking about you, not me.",
"Please don't be so personal."]],
[ /.*/, ["That's very interesting. Do go on.",
"Tell me more.",
"I'm not sure that I understand you fully.",
"Can you elaborate on that?" ]]
(gets;sub(/[.!?,; ]+$/,"");x=Array(S.find{|a|$m=$_.match(a[0])}[1])
puts x[rand(x.size)].gsub(/\d/){$m.to_a[$&.to_i]}) while 9
Some explanation about the shorter Ruby program. Consider this line from the "script":
[ /\bI was (.*)/i, ["Why were you 1?","I can't believe you were 1."]],
Regular expressions are usually enclosed in /.../. The "i" at the end makes it case insensitive, so the regex could just as well have contained "i was". "\b" means that "I" will match only at the beginning of a word; we don't want to
match "cheri was in the room." The parentheses in the regex denote a "capture", the text of which can be recalled later. The dot (.) matches any single character; the star matches any number of the preceding item. So if "\bI was " is found,
(.*)
will match the rest of the string.
I've slightly tweeked the "two lines" of the program and broken them into more lines. p, the programmer's print, has been used to show the state of some variables.
(
gets
Read a line from the keyboard; instead of assigning it to a variable, rely upon Ruby's assigning it to $_.
sub( /[.!?,;\s]+$/, "" )
Since we don't say which string variable the sub should act upon, it acts on $_. We're removing puctuation and whitespace at the end of the user's input.
Assume the user typed "I was thinking."
p $_ # "I was thinking"
The tail of the string has been sanitized.
x = Array(
In the script, sometimes we have only 1 possible response by the computer (i.e., a string); sometimes we have more than 1 response (an array (list) of strings). Array( let's us easily normalize; it converts something that's not an array to an array of 1 element and leaves arrays untouched.
S.find{|a|
Iterate through the script list and return the first item for which our code block returns true. a is the parameter of the code block that receives the value of each item.
$m = $_.match(a[0])
a[0] is the regular expression; we save the match-data in $m.
}[1]
[1] yields the last part of the script item, the computer's response or responses.
)
p x # ["Why were you 1?", "I can't believe you were 1."]
Since the user typed "I was ...", the computer has 2 answers from which to choose.
p $m.class # MatchData
p $m.to_a # ["I was thinking", "thinking"]
.to_a converts match-data to an array. The first item is the complete match; the rest of the items are "captures".
puts x[rand(x.size)].
Randomly choose the computers answer. The "." at the end means a method follows...
gsub( /\d/ ){|s| $m.to_a[s.to_i] } # Why were you thinking?
The .gsub finds every numeral (digit) in the string and replaces it with the corresponding item in the match-data array. "0" would be replaced by the entire match; "1" is replaced by the first capture.
) while 9
"while 0" would work as well. The only untrue things in Ruby are false and nil. You'll have to break out of the loop with a control-key combination.
Please note that this forum doesn't use BBCode; to add code, you need to surround the code by blank lines and indent each line by two spaces or more. The indented lines will be formatted like code, and your * s won't disappear. It would be nice to see the code, but it's unreadable this way.