Having got 3936 LOC through a 4000 LOC implementation, I thought I'd do some recreational hacking and do an old old program I've not looked at for some time - Eliza. You all know Eliza well enough for me not to have to spell it out. The challenge is to implement or dig up an Eliza program (you don't have to write it yourself) in your favourite FPL. Note that the script that drives Eliza's responses should not be counted towards the LOC count. Some constraints.
1. The script itself should be changeable by any novice. That is to say that it should not be a pile of hard-wired code written in the native language of the program or require deep programming skills.
2. The program should receive keyboard input from the user - including the usual punctuation and any characters he wants to enter without crashing.
During the Harrop Wars on comp.lang.lisp a lot of stuff was thrown around about the desirability of pattern matching. The challenge is interesting because it involves a style of pattern-matching called 'segment pattern matching' that is not hard-wired into most FPLs and I'd like to see how well different FPLs cope with something outside the standard.
Oh last thing; don't get too uptight about this. It's only a bit of fun.
> Having got 3936 LOC through a 4000 LOC implementation, I thought I'd > do some recreational > hacking and do an old old program I've not looked at for some time - > Eliza. You all know Eliza well enough for me not to have to spell it > out. The challenge is to implement or dig up an Eliza program (you > don't have to write it yourself) in your favourite FPL. Note that the > script that drives Eliza's responses should not be counted towards the > LOC count. Some constraints.
> 1. The script itself should be changeable by any novice. That is to > say that it should not > be a pile of hard-wired code written in the native language of > the program or require > deep programming skills.
> 2. The program should receive keyboard input from the user - > including the usual punctuation > and any characters he wants to enter without crashing.
> During the Harrop Wars on comp.lang.lisp a lot of stuff was thrown > around about the desirability of pattern matching. The challenge is > interesting because it involves a style of pattern-matching called > 'segment pattern matching' that is not hard-wired into most FPLs and > I'd like to see how well different FPLs cope with something outside > the standard.
> Oh last thing; don't get too uptight about this. It's only a bit of > fun.
> Mark
Well here is my shot at it in Qi. Total LOC excluding script is 70 LOC. You should run it under Qi 9.2 (latest release) because the system function 'read-chars-as-stringlist' had a bug that was patched in that release. My script is very boring ;).
I looked for a Haskell/ML equivalent and found zilch. Norvig's PAIP contains a Lisp version in two files (eliza and eliza1) that is about 150 LOC excluding comments and script.
Mark ___________________________________________________________ (set *script* [ [[X "like" Y] ["Why" "do" "you" "like" Y "?"]] [[X "father" Y] ["Tell me about your father."]] [[X] ["That's very interesting. Do go on."]]])
(define eliza -> (do (output "hi~%?- ") (eliza-loop (user) (value *script*))))
(define remove-if _ [] -> [] F [X | Y] -> (if (F X) (remove-if F Y) [X | (remove-if F Y)]))
(define pmatch User [I R] -> [(pmatch-help I User []) R])
(define pmatch-help X X B -> B [X | Y] [X | Z] B -> (pmatch-help Y Z B) [X | Y] Z B <- (let NilBind (nilbind X B) ValX (value-in X NilBind) (pmatch-help (rep X Y ValX) Z NilBind)) where (variable? X) [X | Y] [W | Z] B <- (pmatch-help [X | Y] Z (consbind X W B)) where (variable? X) _ _ _ -> #\Escape)
(define nilbind X [] -> [[X []]] X [[X V] | B] -> [[X V] | B] X [Y | Z] -> [Y | (nilbind X Z)])
(define consbind X W [] -> [[X [W]]] X W [[X V] | B] -> [[X (append V [W])] | B] X W [Y | B] -> [Y | (consbind X W B)])
(define rep _ [] _ -> [] X [X | Y] V -> (append V (rep X Y V)) X [Y | Z] V -> [Y | (rep X Z V)])
(define value-in X B -> (head (tail (assoc X B))))
> On 22 Feb, 11:49, Mark Tarver <dr.mtar...@ukonline.co.uk> wrote:
> > Having got 3936 LOC through a 4000 LOC implementation, I thought I'd > > do some recreational > > hacking and do an old old program I've not looked at for some time - > > Eliza. You all know Eliza well enough for me not to have to spell it > > out. The challenge is to implement or dig up an Eliza program (you > > don't have to write it yourself) in your favourite FPL. Note that the > > script that drives Eliza's responses should not be counted towards the > > LOC count. Some constraints.
> > 1. The script itself should be changeable by any novice. That is to > > say that it should not > > be a pile of hard-wired code written in the native language of > > the program or require > > deep programming skills.
> > 2. The program should receive keyboard input from the user - > > including the usual punctuation > > and any characters he wants to enter without crashing.
> > During the Harrop Wars on comp.lang.lisp a lot of stuff was thrown > > around about the desirability of pattern matching. The challenge is > > interesting because it involves a style of pattern-matching called > > 'segment pattern matching' that is not hard-wired into most FPLs and > > I'd like to see how well different FPLs cope with something outside > > the standard.
> > Oh last thing; don't get too uptight about this. It's only a bit of > > fun.
> > Mark
> Well here is my shot at it in Qi. Total LOC excluding script is 70 > LOC.
I added some features and wrote it in Ruby.
Sample session:
? Hi. I'm going to be your worst nightmare. Why are you going to be my worst nightmare? ? I think you're full of it. Why do you think I am full of it? ? My mother told me so. Tell me about your mother. ? No. You tell me about yours. We're talking about you, not me. ? You're obviously retarded. Please don't be so personal. ? Damn your eyes! I'm not sure that I understand you fully. ? Understand a kick in the pants? Can you elaborate on that? ? I always ignore idiots. Why do you always ignore idiots? ? I am afraid of dinosaurs. Have you always been afraid of dinosaurs? ? quit
Script = [[:x, ["father",'mother','brother','sister'], :y], ["Tell me about your ", :_, "."]], [[:x, ["am", "i'm"], :y], [["Why are you", :y, "?"], ["Have you always been", :y, "?"]]], [[:x, "I", :y], ["Why do you", :y, "?"]], [[:x, 'you', :y], [["We're talking about you, not me."], ["Please don't be so personal."]]], [[:x], [["That's very interesting. Do go on."], ["Tell me more."], ["I'm not sure that I understand you fully."], ["Can you elaborate on that?"] ]]
def change_person s h = { 'I','you', 'my','your', 'myself','yourself', 'you are','I am', "you're", 'I am' } tmp = s.scan(/you are|you're|\w+|\W+/).map{|s| h[s] or h.invert[s] or s } tmp[-1] = "me" if "I" == tmp[-1] tmp.join end
patterns, symbols, replies = [], [], []
Script.each{|ary| syms = [] patterns << Regexp.new( "^" + ary[0].map{|x| case x when Symbol syms << x "(.*?)" when String "\\b#{ x }\\b" when Array syms << :_ "\\b(#{ x.join('|') })\\b" else ".*?" end }.join + "$", true ) # Case-insensitive. symbols << syms ary[1] = ary[1].sort_by{ rand } if ary[1][0].is_a? Array replies << ary[1]
}
while true print "? " resp = gets.strip.sub(/[.!?,;]+$/, "") break if 'quit' == resp patterns.each_with_index{|pat,i| if match_data = resp.match( pat ) reply = replies[i] if reply[0].is_a? Array # Rotate replies for variety. reply.push( reply.shift ) reply = reply[0] end captures = match_data.captures.map{|s| change_person s} # Create associative array mapping symbols to values. t = Hash[ *symbols[i].zip( captures ).flatten ] puts reply.map{|x| x.is_a?(Symbol) ? t[x] : x }.join break end } end
> On Feb 22, 5:59 am, Mark Tarver <dr.mtar...@ukonline.co.uk> wrote:
> > On 22 Feb, 11:49, Mark Tarver <dr.mtar...@ukonline.co.uk> wrote:
> > > Having got 3936 LOC through a 4000 LOC implementation, I thought I'd > > > do some recreational > > > hacking and do an old old program I've not looked at for some time - > > > Eliza. You all know Eliza well enough for me not to have to spell it > > > out. The challenge is to implement or dig up an Eliza program (you > > > don't have to write it yourself) in your favourite FPL. Note that the > > > script that drives Eliza's responses should not be counted towards the > > > LOC count. Some constraints.
> > > 1. The script itself should be changeable by any novice. That is to > > > say that it should not > > > be a pile of hard-wired code written in the native language of > > > the program or require > > > deep programming skills.
> > > 2. The program should receive keyboard input from the user - > > > including the usual punctuation > > > and any characters he wants to enter without crashing.
> > > During the Harrop Wars on comp.lang.lisp a lot of stuff was thrown > > > around about the desirability of pattern matching. The challenge is > > > interesting because it involves a style of pattern-matching called > > > 'segment pattern matching' that is not hard-wired into most FPLs and > > > I'd like to see how well different FPLs cope with something outside > > > the standard.
> > > Oh last thing; don't get too uptight about this. It's only a bit of > > > fun.
> > > Mark
> > Well here is my shot at it in Qi. Total LOC excluding script is 70 > > LOC.
> I added some features and wrote it in Ruby.
> Sample session:
> ? Hi. I'm going to be your worst nightmare. > Why are you going to be my worst nightmare? > ? I think you're full of it. > Why do you think I am full of it? > ? My mother told me so. > Tell me about your mother. > ? No. You tell me about yours. > We're talking about you, not me. > ? You're obviously retarded. > Please don't be so personal. > ? Damn your eyes! > I'm not sure that I understand you fully. > ? Understand a kick in the pants? > Can you elaborate on that? > ? I always ignore idiots. > Why do you always ignore idiots? > ? I am afraid of dinosaurs. > Have you always been afraid of dinosaurs? > ? quit
> Script = > [[:x, ["father",'mother','brother','sister'], :y], > ["Tell me about your ", :_, "."]], > [[:x, ["am", "i'm"], :y], [["Why are you", :y, "?"], > ["Have you always been", :y, "?"]]], > [[:x, "I", :y], ["Why do you", :y, "?"]], > [[:x, 'you', :y], [["We're talking about you, not me."], > ["Please don't be so personal."]]], > [[:x], [["That's very interesting. Do go on."], > ["Tell me more."], > ["I'm not sure that I understand you fully."], > ["Can you elaborate on that?"] ]]
> def change_person s > h = { 'I','you', 'my','your', 'myself','yourself', > 'you are','I am', "you're", 'I am' } > tmp = s.scan(/you are|you're|\w+|\W+/).map{|s| > h[s] or h.invert[s] or s } > tmp[-1] = "me" if "I" == tmp[-1] > tmp.join > end
> patterns, symbols, replies = [], [], []
> Script.each{|ary| > syms = [] > patterns << Regexp.new( "^" + > ary[0].map{|x| > case x > when Symbol > syms << x > "(.*?)" > when String > "\\b#{ x }\\b" > when Array > syms << :_ > "\\b(#{ x.join('|') })\\b" > else > ".*?" > end > }.join + "$", true ) # Case-insensitive. > symbols << syms > ary[1] = ary[1].sort_by{ rand } if ary[1][0].is_a? Array > replies << ary[1]
> }
> while true > print "? " > resp = gets.strip.sub(/[.!?,;]+$/, "") > break if 'quit' == resp > patterns.each_with_index{|pat,i| > if match_data = resp.match( pat ) > reply = replies[i] > if reply[0].is_a? Array > # Rotate replies for variety. > reply.push( reply.shift ) > reply = reply[0] > end > captures = match_data.captures.map{|s| change_person s} > # Create associative array mapping symbols to values. > t = Hash[ *symbols[i].zip( captures ).flatten ] > puts reply.map{|x| x.is_a?(Symbol) ? t[x] : x }.join > break > end > } > end- Hide quoted text -
> - Show quoted text -
First I've seen Ruby used. Don't understand a damn thing of course :), but 55 LOC puts it top.
On Feb 22, 7:12 pm, Mark Tarver <dr.mtar...@ukonline.co.uk> wrote:
> First I've seen Ruby used. Don't understand a damn thing of > course :), > but 55 LOC puts it top.
> Mark
Symbols begin with ":".
Lists (arrays to Rubyists) are enclosed in [], but they can be omitted sometimes, e.g.: a = 22,33,44
"<<" appends to a list: [6,7,8] << 9 ==>[6, 7, 8, 9]
The index of the first element in a list is 0: [6,7,8][0] ==>6
The array method ".join" is almost self-explanatory: ['tip', '-', 'top'].join ==>"tip-top" ['tip', '-', 'top'].join( '***' ) ==>"tip***-***top"
Anonymous code blocks are enclosed in {..} or do ... end.
The parameters of the code block are enclosed in |...|. In this example, the parameter is "n": [6,7,8].map{|n| n * 2 } ==>[12, 14, 16]
In a different context {...} indicates a hash (associative array): h = {'foo',44, 'bar',55, 'boo',66} ==>{"boo"=>66, "foo"=>44, "bar"=>55} It's clearer but more tedious to separate key-value pairs with "=>": h = {'foo'=>44, 'bar'=>55, 'boo'=>66} ==>{"boo"=>66, "foo"=>44, "bar"=>55}
Let's consider two lines: tmp = s.scan(/you are|you're|\w+|\W+/).map{|s| h[s] or h.invert[s] or s }
".scan" operates on the string s, producing a list of substrings that are matched by the regular expression "/you are|you're|\w+|\W+/". The pipe symbol "|" functions as OR; "\w" matches a word character; "\W" matches a non-word character. Example: "you are willy-nilly, yes?".scan( /you are|you're|\w+|\W+/ ) ==>["you are", " ", "willy", "-", "nilly", ", ", "yes", "?"] Next, ".map" attempts to perform substitutions using the key-value pairs of the hash "h". It makes good use of the method ".invert", which swaps each key with its value: {'foo',22, 'bar',33} ==>{"foo"=>22, "bar"=>33} {'foo',22, 'bar',33}.invert ==>{33=>"bar", 22=>"foo"}
> On Feb 22, 5:59 am, Mark Tarver <dr.mtar...@ukonline.co.uk> wrote: >> On 22 Feb, 11:49, Mark Tarver <dr.mtar...@ukonline.co.uk> wrote:
>> > Having got 3936 LOC through a 4000 LOC implementation, I thought I'd >> > do some recreational >> > hacking and do an old old program I've not looked at for some time - >> > Eliza. You all know Eliza well enough for me not to have to spell it >> > out. The challenge is to implement or dig up an Eliza program (you >> > don't have to write it yourself) in your favourite FPL. Note that the >> > script that drives Eliza's responses should not be counted towards the >> > LOC count. Some constraints.
>> > 1. The script itself should be changeable by any novice. That is to >> > say that it should not >> > be a pile of hard-wired code written in the native language of >> > the program or require >> > deep programming skills.
>> > 2. The program should receive keyboard input from the user - >> > including the usual punctuation >> > and any characters he wants to enter without crashing.
>> > During the Harrop Wars on comp.lang.lisp a lot of stuff was thrown >> > around about the desirability of pattern matching. The challenge is >> > interesting because it involves a style of pattern-matching called >> > 'segment pattern matching' that is not hard-wired into most FPLs and >> > I'd like to see how well different FPLs cope with something outside >> > the standard.
>> > Oh last thing; don't get too uptight about this. It's only a bit of >> > fun.
>> > Mark
>> Well here is my shot at it in Qi. Total LOC excluding script is 70 >> LOC.
> I added some features and wrote it in Ruby.
> Sample session:
> ? Hi. I'm going to be your worst nightmare. > Why are you going to be my worst nightmare? > ? I think you're full of it. > Why do you think I am full of it? > ? My mother told me so. > Tell me about your mother. > ? No. You tell me about yours. > We're talking about you, not me. > ? You're obviously retarded. > Please don't be so personal. > ? Damn your eyes! > I'm not sure that I understand you fully. > ? Understand a kick in the pants? > Can you elaborate on that? > ? I always ignore idiots. > Why do you always ignore idiots? > ? I am afraid of dinosaurs. > Have you always been afraid of dinosaurs? > ? quit
> Script = > [[:x, ["father",'mother','brother','sister'], :y], > ["Tell me about your ", :_, "."]], > [[:x, ["am", "i'm"], :y], [["Why are you", :y, "?"], > ["Have you always been", :y, "?"]]], > [[:x, "I", :y], ["Why do you", :y, "?"]], > [[:x, 'you', :y], [["We're talking about you, not me."], > ["Please don't be so personal."]]], > [[:x], [["That's very interesting. Do go on."], > ["Tell me more."], > ["I'm not sure that I understand you fully."], > ["Can you elaborate on that?"] ]]
> def change_person s > h = { 'I','you', 'my','your', 'myself','yourself', > 'you are','I am', "you're", 'I am' } > tmp = s.scan(/you are|you're|\w+|\W+/).map{|s| > h[s] or h.invert[s] or s } > tmp[-1] = "me" if "I" == tmp[-1] > tmp.join > end
> patterns, symbols, replies = [], [], []
> Script.each{|ary| > syms = [] > patterns << Regexp.new( "^" + > ary[0].map{|x| > case x > when Symbol > syms << x > "(.*?)" > when String > "\\b#{ x }\\b" > when Array > syms << :_ > "\\b(#{ x.join('|') })\\b" > else > ".*?" > end > }.join + "$", true ) # Case-insensitive. > symbols << syms > ary[1] = ary[1].sort_by{ rand } if ary[1][0].is_a? Array > replies << ary[1] > }
> while true > print "? " > resp = gets.strip.sub(/[.!?,;]+$/, "") > break if 'quit' == resp > patterns.each_with_index{|pat,i| > if match_data = resp.match( pat ) > reply = replies[i] > if reply[0].is_a? Array > # Rotate replies for variety. > reply.push( reply.shift ) > reply = reply[0] > end > captures = match_data.captures.map{|s| change_person s} > # Create associative array mapping symbols to values. > t = Hash[ *symbols[i].zip( captures ).flatten ] > puts reply.map{|x| x.is_a?(Symbol) ? t[x] : x }.join > break > end > } > end
D:(("1 father|mother|brother|sister 2";"tell me about your 0."); ("1 am|i'm 2";"why are you 2|have you always been 2?"); ("1 i 2";"why do you 2?"); ("1 you 2";"we're talking about you, not me.|please don't be so personal"))
E:"|"vs"that's very interesting. do go on.| tell me more| i'm not sure i understand you fully| can you elaborate on that?"
S:{(`$"|"vs first w where not b;raze w where b:(first each w:" "vs x)in"0123456789")}each D[;0] R:"|"vs'D[;1] P:{(y,x,z)(x,y)?z}[`$("you";"your";"yourself";"i'm";"i am");`$("i";"my";"myself";"you are";"you're")]
e:{w:(`$" "vs x except".?")except`; / words except punctuation, blanks b:w in\:/:S[;0]; / boolean mask: words X rules if[count[b]=i:(any each b)?1b;:E first 1?count E]; / if no key match on a rule, early exit p:1_'(0,1+k:b[i]?1b)_`,w; / split input on key r:{x first 1?count x}R i; / pick a rule n:r first raze r ss/:"0123456789"; / which part of input to replace ssr[r;n;" "sv string(),P each(w[k],p)"I"$n]} / construct reply
in k it's about half the size, but i'll spare you.
>> > Having got 3936 LOC through a 4000 LOC implementation, I thought I'd >> > do some recreational >> > hacking and do an old old program I've not looked at for some time - >> > Eliza. You all know Eliza well enough for me not to have to spell it >> > out. The challenge is to implement or dig up an Eliza program (you >> > don't have to write it yourself) in your favourite FPL. Note that the >> > script that drives Eliza's responses should not be counted towards the >> > LOC count. Some constraints.
>> > 1. The script itself should be changeable by any novice. That is to >> > say that it should not >> > be a pile of hard-wired code written in the native language of >> > the program or require >> > deep programming skills.
>> > 2. The program should receive keyboard input from the user - >> > including the usual punctuation >> > and any characters he wants to enter without crashing.
>> > During the Harrop Wars on comp.lang.lisp a lot of stuff was thrown >> > around about the desirability of pattern matching. The challenge is >> > interesting because it involves a style of pattern-matching called >> > 'segment pattern matching' that is not hard-wired into most FPLs and >> > I'd like to see how well different FPLs cope with something outside >> > the standard.
>> > Oh last thing; don't get too uptight about this. It's only a bit of >> > fun.
>> > Mark
>> Well here is my shot at it in Qi. Total LOC excluding script is 70 >> LOC.
> I added some features and wrote it in Ruby.
> Sample session:
> ? Hi. I'm going to be your worst nightmare. > Why are you going to be my worst nightmare? > ? I think you're full of it. > Why do you think I am full of it? > ? My mother told me so. > Tell me about your mother. > ? No. You tell me about yours. > We're talking about you, not me. > ? You're obviously retarded. > Please don't be so personal. > ? Damn your eyes! > I'm not sure that I understand you fully. > ? Understand a kick in the pants? > Can you elaborate on that? > ? I always ignore idiots. > Why do you always ignore idiots? > ? I am afraid of dinosaurs. > Have you always been afraid of dinosaurs? > ? quit
> Script = > [[:x, ["father",'mother','brother','sister'], :y], > ["Tell me about your ", :_, "."]], > [[:x, ["am", "i'm"], :y], [["Why are you", :y, "?"], > ["Have you always been", :y, "?"]]], > [[:x, "I", :y], ["Why do you", :y, "?"]], > [[:x, 'you', :y], [["We're talking about you, not me."], > ["Please don't be so personal."]]], > [[:x], [["That's very interesting. Do go on."], > ["Tell me more."], > ["I'm not sure that I understand you fully."], > ["Can you elaborate on that?"] ]]
> def change_person s > h = { 'I','you', 'my','your', 'myself','yourself', > 'you are','I am', "you're", 'I am' } > tmp = s.scan(/you are|you're|\w+|\W+/).map{|s| > h[s] or h.invert[s] or s } > tmp[-1] = "me" if "I" == tmp[-1] > tmp.join > end
> patterns, symbols, replies = [], [], []
> Script.each{|ary| > syms = [] > patterns << Regexp.new( "^" + > ary[0].map{|x| > case x > when Symbol > syms << x > "(.*?)" > when String > "\\b#{ x }\\b" > when Array > syms << :_ > "\\b(#{ x.join('|') })\\b" > else > ".*?" > end > }.join + "$", true ) # Case-insensitive. > symbols << syms > ary[1] = ary[1].sort_by{ rand } if ary[1][0].is_a? Array > replies << ary[1] > }
> while true > print "? " > resp = gets.strip.sub(/[.!?,;]+$/, "") > break if 'quit' == resp > patterns.each_with_index{|pat,i| > if match_data = resp.match( pat ) > reply = replies[i] > if reply[0].is_a? Array > # Rotate replies for variety. > reply.push( reply.shift ) > reply = reply[0] > end > captures = match_data.captures.map{|s| change_person s} > # Create associative array mapping symbols to values. > t = Hash[ *symbols[i].zip( captures ).flatten ] > puts reply.map{|x| x.is_a?(Symbol) ? t[x] : x }.join > break > end > } > end
> D:(("1 father|mother|brother|sister 2";"tell me about your 0."); > ("1 am|i'm 2";"why are you 2|have you always been 2?"); > ("1 i 2";"why do you 2?"); > ("1 you 2";"we're talking about you, not me.|please don't be so > personal"))
> E:"|"vs"that's very interesting. do go on.| > tell me more| > i'm not sure i understand you fully| > can you elaborate on that?"
> S:{(`$"|"vs first w where not b;raze w where b:(first each w:" "vs > x)in"0123456789")}each D[;0] > R:"|"vs'D[;1] > P:{(y,x,z)(x,y)?z}[`$("you";"your";"yourself";"i'm";"i > am");`$("i";"my";"myself";"you are";"you're")]
> e:{w:(`$" "vs x except".?")except`; / words except > punctuation, blanks > b:w in\:/:S[;0]; / boolean mask: > words X rules > if[count[b]=i:(any each b)?1b;:E first 1?count E]; / if no key > match on a rule, early exit > p:1_'(0,1+k:b[i]?1b)_`,w; / split input on key > r:{x first 1?count x}R i; / pick a rule > n:r first raze r ss/:"0123456789"; / which part of > input to replace > ssr[r;n;" "sv string(),P each(w[k],p)"I"$n]} / construct reply
> in k it's about half the size, but i'll spare you.
I'd love to see it, or a link to any other sample K code. I recall seeing a one page implementation of a spreadsheet, that was wild.
if no keyword match in A, then return a default response from E else if no substitution in the response c picked from C, return c else substitute and return
>> D:(("1 father|mother|brother|sister 2";"tell me about your 0."); >> ("1 am|i'm 2";"why are you 2|have you always been 2?"); >> ("1 i 2";"why do you 2?"); >> ("1 you 2";"we're talking about you, not me.|please don't be so >> personal"))
>> E:"|"vs"that's very interesting. do go on.| >> tell me more| >> i'm not sure i understand you fully| >> can you elaborate on that?"
>> S:{(`$"|"vs first w where not b;raze w where b:(first each w:" "vs >> x)in"0123456789")}each D[;0] >> R:"|"vs'D[;1] >> P:{(y,x,z)(x,y)?z}[`$("you";"your";"yourself";"i'm";"i >> am");`$("i";"my";"myself";"you are";"you're")]
>> e:{w:(`$" "vs x except".?")except`; / words except >> punctuation, blanks >> b:w in\:/:S[;0]; / boolean mask: >> words X rules >> if[count[b]=i:(any each b)?1b;:E first 1?count E]; / if no key >> match on a rule, early exit >> p:1_'(0,1+k:b[i]?1b)_`,w; / split input on key >> r:{x first 1?count x}R i; / pick a rule >> n:r first raze r ss/:"0123456789"; / which part of >> input to replace >> ssr[r;n;" "sv string(),P each(w[k],p)"I"$n]} / construct reply
>> in k it's about half the size, but i'll spare you.
> I'd love to see it, or a link to any other sample K code. I recall > seeing a one page implementation of a spreadsheet, that was wild.
I'm not a programmer. I just wanted to have some fun, so I tried to write a Mathematica version based on the Ruby version (the only one I actually understood). I'm sure that this can be improved and written in a nicer way, but here it goes: :-)
script = { {__, u : "father" | "mother" | "brother" | "sister", ___} :> {"Tell me about your " <> v <> "."}, {___, "am" | "i'm", u__} :> {"Why are you " <> v <> "?", "Have you always been " <> v <> "?"}, {___, "i", u__} :> {"Why do you " <> v <> "?"}, {___, "you", ___} :> {"We're talking about you, not me", "Please don't be so personal."}, {u___} :> {"That's very interesting. Do go on.", "Tell me more.", "I'm not sure that I understand you fully.", "Can you elaborate on that?", "Why do you think that " <> v <> "?"}
Wonderful. I will post this over on the Arc forum, I think they are headed your way.
So how big a chunk can a K guru toss off without taking a breath, if you will? I suppose it is like any language, developing bits and pieces before connecting it all. My suspicion is one can get off more functionality simply from the lower character count, once they are all known fluently.
> if no keyword match in A, then return a default response from E > else if no substitution in the response c picked from C, return c > else substitute and return
>>> D:(("1 father|mother|brother|sister 2";"tell me about your 0."); >>> ("1 am|i'm 2";"why are you 2|have you always been 2?"); >>> ("1 i 2";"why do you 2?"); >>> ("1 you 2";"we're talking about you, not me.|please don't be so >>> personal"))
>>> E:"|"vs"that's very interesting. do go on.| >>> tell me more| >>> i'm not sure i understand you fully| >>> can you elaborate on that?"
>>> S:{(`$"|"vs first w where not b;raze w where b:(first each w:" "vs >>> x)in"0123456789")}each D[;0] >>> R:"|"vs'D[;1] >>> P:{(y,x,z)(x,y)?z}[`$("you";"your";"yourself";"i'm";"i >>> am");`$("i";"my";"myself";"you are";"you're")]
>>> e:{w:(`$" "vs x except".?")except`; / words >>> except punctuation, blanks >>> b:w in\:/:S[;0]; / boolean >>> mask: words X rules >>> if[count[b]=i:(any each b)?1b;:E first 1?count E]; / if no key >>> match on a rule, early exit >>> p:1_'(0,1+k:b[i]?1b)_`,w; / split input >>> on key >>> r:{x first 1?count x}R i; / pick a rule >>> n:r first raze r ss/:"0123456789"; / which part >>> of input to replace >>> ssr[r;n;" "sv string(),P each(w[k],p)"I"$n]} / construct >>> reply
>>> in k it's about half the size, but i'll spare you.
>> I'd love to see it, or a link to any other sample K code. I recall >> seeing a one page implementation of a spreadsheet, that was wild.
> Having got 3936 LOC through a 4000 LOC implementation, I thought I'd > do some recreational > hacking and do an old old program I've not looked at for some time - > Eliza. You all know Eliza well enough for me not to have to spell it > out. The challenge is to implement or dig up an Eliza program (you > don't have to write it yourself) in your favourite FPL. Note that the > script that drives Eliza's responses should not be counted towards the > LOC count. Some constraints.
> 1. The script itself should be changeable by any novice. That is to > say that it should not > be a pile of hard-wired code written in the native language of > the program or require > deep programming skills.
> 2. The program should receive keyboard input from the user - > including the usual punctuation > and any characters he wants to enter without crashing.
> During the Harrop Wars on comp.lang.lisp a lot of stuff was thrown > around about the desirability of pattern matching. The challenge is > interesting because it involves a style of pattern-matching called > 'segment pattern matching' that is not hard-wired into most FPLs and > I'd like to see how well different FPLs cope with something outside > the standard.
Here's a shorter Ruby version. Regular expressions are used in the script.
Script = [ /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?" ]]
def change_person s h = { 'I','you', 'my','your', 'myself','yourself', 'you are','I am', "you're","I am" } s.scan(/you are|you're|\w+|\W+/).map{|s| h[s] or h.invert[s] or s }.join.sub( / I$/, " me" ) end
while true print "? " break if (resp = gets.strip.sub(/[.!?,;]+$/, "")) == 'quit' Script.each{|ary| if (md = resp.match( ary[0] ).to_a) != [] t = Array( ary[1] ) # Replies are rotated for variety. puts t.push(t.shift)[0].gsub(/\d/){change_person md[$&.to_i]} break end } end
Wow, looks like APL - also famous for inscrutable 1 liners.
Nothing from the big players Haskell, O'Caml & ML so far.
So far I've got
Lisp 150 LOC ................ Peter Norvig Qi (slightly revised) 63 LOC .................Mark Tarver Ruby 53 LOC .................William James Q 10 LOC ..................Steven Apter K 3 LOC ...................Steven Apter
Lisp 150 LOC ................ Peter Norvig Qi (slightly revised) 63 LOC .................Mark Tarver Ruby 53 LOC .................William James Q 10 LOC ..................Steven Apter K 3 LOC ...................Steven Apter Ruby (again) 17 LOC ...................William James Mathematica 20 LOC .................. Szabolcs
> Wonderful. I will post this over on the Arc forum, I think they are > headed your way.
> So how big a chunk can a K guru toss off without taking a breath, if you > will? I suppose it is like any language, developing bits and pieces > before connecting it all. My suspicion is one can get off more > functionality simply from the lower character count, once they are all > known fluently.
i now prefer developing in q, which is k without ambivalence (symbols denote binary functions only, unary functions have keywords.)
based on what i've seen, my guess is that this problem has a k solution half the size of mine, given a better representation of the prompt-respond script. in general, that seems to be the key to good k (and as mark observed, APL) programming: tune your data to the primitives.
>> it's just a single eye-watering case statement:
>> if no keyword match in A, then return a default response from E >> else if no substitution in the response c picked from C, return c >> else substitute and return
>>>> D:(("1 father|mother|brother|sister 2";"tell me about your 0."); >>>> ("1 am|i'm 2";"why are you 2|have you always been 2?"); >>>> ("1 i 2";"why do you 2?"); >>>> ("1 you 2";"we're talking about you, not me.|please don't be so >>>> personal"))
>>>> E:"|"vs"that's very interesting. do go on.| >>>> tell me more| >>>> i'm not sure i understand you fully| >>>> can you elaborate on that?"
>>>> S:{(`$"|"vs first w where not b;raze w where b:(first each w:" "vs >>>> x)in"0123456789")}each D[;0] >>>> R:"|"vs'D[;1] >>>> P:{(y,x,z)(x,y)?z}[`$("you";"your";"yourself";"i'm";"i >>>> am");`$("i";"my";"myself";"you are";"you're")]
>>>> e:{w:(`$" "vs x except".?")except`; / words >>>> except punctuation, blanks >>>> b:w in\:/:S[;0]; / boolean >>>> mask: words X rules >>>> if[count[b]=i:(any each b)?1b;:E first 1?count E]; / if no key >>>> match on a rule, early exit >>>> p:1_'(0,1+k:b[i]?1b)_`,w; / split input >>>> on key >>>> r:{x first 1?count x}R i; / pick a rule >>>> n:r first raze r ss/:"0123456789"; / which part >>>> of input to replace >>>> ssr[r;n;" "sv string(),P each(w[k],p)"I"$n]} / construct >>>> reply
>>>> in k it's about half the size, but i'll spare you.
>>> I'd love to see it, or a link to any other sample K code. I recall >>> seeing a one page implementation of a spreadsheet, that was wild.
On Feb 24, 6:06 am, Mark Tarver <dr.mtar...@ukonline.co.uk> wrote:
> Sorry I missed a couple of later attempts
> Lisp 150 LOC ................ Peter Norvig > Qi (slightly revised) 63 LOC .................Mark Tarver > Ruby 53 LOC .................William James > Q 10 LOC ..................Steven Apter > K 3 LOC ...................Steven Apter > Ruby (again) 17 LOC ...................William James > Mathematica 20 LOC .................. Szabolcs
> Mark
The K version doesn't do everything that the Ruby versions do. It doesn't change "I" in the user's response to "you" when throwing it back at the user, for example.
When there are multiple possible replies to a user's input, the Ruby program rotates those replies so that not one will be repeated until all have been used.
e"hi. i'm going to be your worst nightmare" e"i think you're full of it." e"my mother told me so." e"no. you tell me about yours." e"you're obviously retarded." e"damn your eyes!" e"understand a kick in the pants?" e"i always ignore idiots." e"i am afraid of dinosaurs"
responses:
"have you always been going to be my worst nightmare? "why do you think i am full of it?" "tell me about your mother." "please don't be so personal." "can you elaborate on that?" "can you elaborate on that?" "that's very interesting. do go on." "why do you always ignore idiots?" "have you always been afraid of dinosaurs?"
a few observations:
the 'i' function gives you prompt-and-respond in the console by call 'e' until the user gives no prompt:
>> Lisp 150 LOC ................ Peter Norvig >> Qi (slightly revised) 63 LOC .................Mark Tarver >> Ruby 53 LOC .................William James >> Q 10 LOC ..................Steven Apter >> K 3 LOC ...................Steven Apter >> Ruby (again) 17 LOC ...................William James >> Mathematica 20 LOC .................. Szabolcs
>> Mark
> The K version doesn't do everything that the Ruby > versions do. It doesn't change "I" in the user's > response to "you" when throwing it back at the user, > for example.
> When there are multiple possible replies to a user's > input, the Ruby program rotates those replies so that > not one will be repeated until all have been used.
> if no keyword match in A, then return a default response from E > else if no substitution in the response c picked from C, return c > else substitute and return
This does so little that it should be no more than 2 lines. In Ruby:
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
Although cryptic, it should make more sense to most programmers than the K code.
the use of regular expressions can certainly make the code smaller, as your example demonstrates. i wonder though whether it is entirely in keeping with the spirit of the exercise, which requires that:
1. The script itself should be changeable by any novice. That is to say that it should not be a pile of hard-wired code written in the native language of the program or require deep programming skills.
not that it would help make the k code shorter, since regexp isn't built into k.
>> it's just a single eye-watering case statement:
>> if no keyword match in A, then return a default response from E >> else if no substitution in the response c picked from C, return c >> else substitute and return
> This does so little that it should be no more than 2 lines. > In Ruby:
> 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
> Although cryptic, it should make more sense to most programmers > than the K code.
> e"hi. i'm going to be your worst nightmare" > e"i think you're full of it." > e"my mother told me so." > e"no. you tell me about yours." > e"you're obviously retarded." > e"damn your eyes!" > e"understand a kick in the pants?" > e"i always ignore idiots." > e"i am afraid of dinosaurs"
> responses:
> "have you always been going to be my worst nightmare? > "why do you think i am full of it?" > "tell me about your mother." > "please don't be so personal." > "can you elaborate on that?" > "can you elaborate on that?" > "that's very interesting. do go on." > "why do you always ignore idiots?" > "have you always been afraid of dinosaurs?"
I don't see "you", "your", "my", "myself", "yourself", etc. So how can it change "your" to "my"? I was talking about the K program; you're talking about the Q program.
> a few observations:
> the 'i' function gives you prompt-and-respond in the console by > call 'e' until the user gives no prompt:
> i:{while[count r:read0 0;-1"? ",e r;]}
I was talking about the K program; you're talking about the Q program.
> >> Lisp 150 LOC ................ Peter Norvig > >> Qi (slightly revised) 63 LOC .................Mark Tarver > >> Ruby 53 LOC .................William James > >> Q 10 LOC ..................Steven Apter > >> K 3 LOC ...................Steven Apter > >> Ruby (again) 17 LOC ...................William James > >> Mathematica 20 LOC .................. Szabolcs
> >> Mark
> > The K version doesn't do everything that the Ruby > > versions do. It doesn't change "I" in the user's > > response to "you" when throwing it back at the user, > > for example.
> > When there are multiple possible replies to a user's > > input, the Ruby program rotates those replies so that > > not one will be repeated until all have been used.
On Feb 24, 8:37 am, "Stevan Apter" <encryptednos...@nsl.com> wrote:
> the use of regular expressions can certainly make the code smaller, > as your example demonstrates. i wonder though whether it is entirely > in keeping with the spirit of the exercise, which requires that:
> 1. The script itself should be changeable by any novice. That is to > say that it should not > be a pile of hard-wired code written in the native language of > the program or require > deep programming skills.
I understand your misgivings, and I considered that myself. Regular expressions are used in many programming languages and Unix utilities (awk, Perl, grep, sed, etc.). All good text editors support them, so anyone who is serious about using computers should familiarize himself with them. For example, say he's editing a large file and wants to find a line that has "foo" followed later in the line by "bar"; he'd simply search for "foo.*bar". How would that be done without regular expressions? I don't think that learning about regular expressions means learning "deep programming skills". But yes, using my shorter program would be harder for a novice.
> >> it's just a single eye-watering case statement:
> >> if no keyword match in A, then return a default response from E > >> else if no substitution in the response c picked from C, return c > >> else substitute and return
> > This does so little that it should be no more than 2 lines. > > In Ruby:
> > 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
> > Although cryptic, it should make more sense to most programmers > > than the K code.
Mark Tarver <dr.mtar...@ukonline.co.uk> writes: > Sorry I missed a couple of later attempts
> Lisp 150 LOC ................ Peter Norvig > Qi (slightly revised) 63 LOC .................Mark Tarver > Ruby 53 LOC .................William James > Q 10 LOC ..................Steven Apter > K 3 LOC ...................Steven Apter > Ruby (again) 17 LOC ...................William James > Mathematica 20 LOC .................. Szabolcs
> Mark
So K wins the competition in brevity, but does the ability to squeeze code into illegible character blobs say anything about the suitability of a programming language for writing readable and understandable programs? I'd rather follow the principle that "Programs must be written for people to read, and only incidentally for machines to execute."...
-- Dimitre Liotev (format t "~{~a~}" (reverse '("et" "n" "in." "a" "zn" "@" "l" "d")))