diff --git a/examples/random_scripts.rb b/examples/random_scripts.rb new file mode 100644 index 0000000..32d6515 --- /dev/null +++ b/examples/random_scripts.rb @@ -0,0 +1,75 @@ +#encoding: utf-8 +require_relative '../lib/duck' +require 'timeout' +include Duck + +############ +# A simple experiment in the diversity of Duck scripts. +# +# We generate Duck scripts from random tokens, run them with different inputs 'x' +# to observe how many act differently with just the change in inputs value. + + +###### +# some conveniences, first: + +ARITHMETIC_TOKENS = ["+","-","*","/","inc","dec","if"] + ['k','f','x'] * 5 +ALL_MESSAGES = + [Assembler,Binder,Bool,Closure,Collector, + Decimal,Error,Int,Interpreter,Item,Iterator,List,Local, + Message,Number,Pipe,Proxy,Script,Span,Variable].inject([]) {|messages,klass| (messages | klass.recognized_messages)} + +@everything = + ALL_MESSAGES + ['m','k','b','f','x'] * 10 + +def random_tokens(how_many,source_list) + tokens = how_many.times.collect do + t = source_list.sample + case + when t=="k" # some random integer + val = "#{(0..9).to_a.sample}" + rand < 0.5 ? val : "-#{val}" + when t=="m" # some random local message + "_#{('a'..'z').to_a.sample}" + when t=="b" # some random boolean + rand < 0.5 ? "T" : "F" + when t=="f" # some random float + val = "#{(0..9).to_a.sample}.#{(0..9).to_a.sample}" + rand < 0.5 ? val : "-#{val}" + else # just what it says + t.to_s + end + end +end + + +def random_script(how_many, source_list=@everything) + parts = random_tokens(how_many, source_list) + parts.join(" ") +end + +############ +# +# We generate 500 random 50-token scripts +# set variable :x1 and :x2 +# run each script, and count the number of ticks execution takes, +# and report the top two Number values present on the interpreter when it's done + + +puts "trial, ticks(x1), ticks(x2), y1(x1), y1(x2), y2(x1), y2(x2)" +(0..500).each do |i| + pts = 100 + x1 = 16 + x2 = 19 + script = random_script(pts) + begin + Timeout::timeout(120) do + random_one = interpreter(script:script, binder:{x:int(x1)}).run + random_two = interpreter(script:script, binder:{x:int(x2)}).run + puts"#{i}: #{random_one.ticks}, #{random_two.ticks}, #{random_one.emit!(Number)||'nil'}, #{random_two.emit!(Number)||'nil'}, #{random_one.emit!(Number)||'nil'}, #{random_two.emit!(Number)||'nil'}" + end + rescue Exception => e + puts "ERROR:#{e.message} -- #{e.backtrace} #{script.inspect}" + raise + end +end \ No newline at end of file diff --git a/examples/trivial_gp.rb b/examples/trivial_gp.rb index 6aa25d5..527f2d3 100644 --- a/examples/trivial_gp.rb +++ b/examples/trivial_gp.rb @@ -117,8 +117,8 @@ def evaluate(x_y_pairs_hash={},out_file = nil) NUMBERLESS_TOKENS = ["+","-","*","/","inc","dec"]+['x']*4 # EXERCISE (one hand tied) ALL_MESSAGES = [Assembler,Binder,Bool,Closure,Collector, - Decimal,Error,Int,Interpreter,Item,List, - Message,Number,Pipe,Script,Variable].inject([]) {|messages,klass| (messages | klass.recognized_messages)} + Decimal,Error,Int,Interpreter,Item,Iterator,List,Local, + Message,Number,Pipe,Proxy,Script,Span,Variable].inject([]) {|messages,klass| (messages | klass.recognized_messages)} @experiment_tokens = ALL_MESSAGES + SIMPLE_TOKENS @@ -140,10 +140,10 @@ def random_tokens(how_many,source_list) end -pop_size = 100 +pop_size = 50 updates = pop_size*3 cycles = 500 -standard_length = 100 +standard_length = 40 population = pop_size.times.collect {Answer.new(random_tokens(standard_length,@experiment_tokens))} File.open("./data/scores.csv", "w") do |tracefile| @@ -180,7 +180,7 @@ def random_tokens(how_many,source_list) mom, dad = population[mom_index], population[dad_index] # EXERCISE (recombination) - crossover1,crossover2 = mom.crossover_result(dad, false) + crossover1,crossover2 = mom.crossover_result(dad) baby1 = Answer.new(crossover1).mutant(3,@experiment_tokens) # EXERCISE (ontological creep) baby2 = Answer.new(crossover2).mutant(3,@experiment_tokens) diff --git a/lib/assembler_class.rb b/lib/assembler_class.rb index 9cd85b4..a6442e5 100644 --- a/lib/assembler_class.rb +++ b/lib/assembler_class.rb @@ -85,9 +85,7 @@ def count_a_tick def give_ticks - recorded_ticks = @ticks - @ticks = 0 - return recorded_ticks + return @ticks end diff --git a/lib/binder_class.rb b/lib/binder_class.rb index 4cb8195..5bee152 100644 --- a/lib/binder_class.rb +++ b/lib/binder_class.rb @@ -2,13 +2,20 @@ module Duck class Binder < List attr_accessor :contents - + + def initialize(contents=[]) @contents = contents @needs = [] end + def deep_copy + Binder.new( @contents.collect {|item| item.deep_copy}) + end + + + def recognize_message?(string) mine_and_theirs = contents_recognize_message?(string) || Binder.recognized_messages.include?(string.intern) end @@ -38,7 +45,7 @@ def index_of_next_respondent_to(msg) def produce_respondent(msg) which = index_of_next_respondent_to(msg.intern) if which.nil? - result = personally_recognizes?(msg) ? self : nil + result = personally_recognizes?(msg) ? self.deep_copy : nil else if @contents[which].kind_of?(Binder) result = @contents[which].produce_respondent(msg) diff --git a/lib/bool_class.rb b/lib/bool_class.rb index c64130a..fc52861 100644 --- a/lib/bool_class.rb +++ b/lib/bool_class.rb @@ -54,5 +54,10 @@ def to_s duck_handle :to_int do Int.new(@value ? 1 : 0) end + + + duck_handle :to_script do + Script.new(@value ? 'T' : 'F') + end end end \ No newline at end of file diff --git a/lib/decimal_class.rb b/lib/decimal_class.rb index 5ae0d02..c061ade 100644 --- a/lib/decimal_class.rb +++ b/lib/decimal_class.rb @@ -1,10 +1,9 @@ #encoding: utf-8 - -def decimal(value) - Decimal.new(value) -end - module Duck + def decimal(value) + Decimal.new(value) + end + class Decimal < Number def to_s "#{@value}" @@ -38,6 +37,19 @@ def to_s end + duck_handle :count do + Iterator.new(start:0.0, end:@value, inc:1.0,:response => :index).run + end + + + duck_handle :count_by do + Closure.new(["neg"], "#{self.inspect}.count_by(?)") do |number| + Iterator.new(start:0.0, end:@value, inc:number.value.to_f, :response => :index).run + end + end + + + duck_handle :rand do case when @value > 0.0 diff --git a/lib/int_class.rb b/lib/int_class.rb index 56eef24..9338fb9 100644 --- a/lib/int_class.rb +++ b/lib/int_class.rb @@ -1,11 +1,10 @@ #encoding: utf-8 - -def int(value) - Int.new(value) -end - - module Duck + + def int(value) + Int.new(value) + end + class Int < Number def type_cast_result(arg, operator) arg.class == Decimal ? @@ -58,12 +57,24 @@ def to_s i = self.value Closure.new(["be"],"#{i} of ?") do |item| item.to_s.length * i <= @@result_size_limit ? - i.times.collect {|i| item.clone} : + i.times.collect {|i| item.deep_copy} : Error.new("OVERSIZED RESULT") end end - - + + + duck_handle :count do + Iterator.new(start:0, end:@value, inc:1,:response => :index).run + end + + + duck_handle :count_by do + Closure.new(["inc"],"#{self.inspect}.count_by(?)") do |increment| + Iterator.new(start:0, end:@value, inc:increment.value, :response => :index).run + end + end + + duck_handle :dec do Int.new @value - 1 end @@ -89,7 +100,7 @@ def to_s duck_handle :times_do do Closure.new(["be"], "#{self.inspect}.times_do(?)") do |item| @value > 0 ? - [Iterator.new(start:0, end:@value, contents:[item]), Message.new("run")] : + Iterator.new(start:0, end:@value, contents:[item]).run : self end end diff --git a/lib/interpreter_class.rb b/lib/interpreter_class.rb index 34febce..7bbb92c 100644 --- a/lib/interpreter_class.rb +++ b/lib/interpreter_class.rb @@ -193,12 +193,7 @@ def trace_used_as_arg(position) def trace_uses_arg(position) write_to_trace " ... grabbed the #{@contents[position].class} #{@contents[position].inspect}" unless position.nil? end - - - def trace_entire_buffer - write_to_trace " ... the buffer is now #{@buffer.inspect}" - end - + def trace_pushed_item(item) @@ -215,7 +210,6 @@ def process_next_buffer_item unless buffer.empty? count_a_tick @staged_item = @buffer.delete_at(0) - trace_entire_buffer if @trace trace_staging if @trace next_arg_position = next_contents_index_wanted_by(@staged_item) if next_arg_position.nil? diff --git a/lib/iterator_class.rb b/lib/iterator_class.rb index a7236fd..b97d109 100644 --- a/lib/iterator_class.rb +++ b/lib/iterator_class.rb @@ -8,6 +8,7 @@ class Iterator < List attr_accessor :index attr_accessor :contents attr_accessor :min_value, :max_value + attr_accessor :response def initialize(args = {}) default_args = {start:0, end:0, inc:1, index:nil} @@ -23,6 +24,7 @@ def initialize(args = {}) end @index = args[:index] || @start @contents = args[:contents] || [] + @response = args[:response] || :contents @min_value,@max_value = [@start,@end].minmax @needs = [] end @@ -34,21 +36,25 @@ def deep_copy end:@end, inc:@inc, index:@index, - contents:@contents.collect {|item| item.deep_copy} + contents:@contents.collect {|item| item.deep_copy}, + response:@response, ) end def to_s - "(#{@start}..#{@index}..#{@end})=>#{@contents.inspect}" + rounded_index = @index.kind_of?(Float) ? "~(#{@index.round(3)})" : "#{@index}" + return "(#{@start}..#{rounded_index}..#{@end})=>#{@contents}" end + def index_in_range? @start < @end ? @min_value <= @index && @index < @max_value : @min_value < @index && @index <= @max_value end + def reset_index_within_range if !index_in_range? @index = @min_value if @index < @min_value @@ -56,14 +62,48 @@ def reset_index_within_range end end + + def output_value + case @response + when :element + output = @contents.empty? ? [] : [@contents[@index % @contents.length].deep_copy] + when :index + output = @index.kind_of?(Float) ? [Decimal.new(@index)] : [Int.new(@index)] + else + output = @contents.collect {|item| item.deep_copy} + end + return output + end + + # DUCK HANDLERS duck_handle :∪ do Closure.new(["shatter"], "#{self.inspect} ∪ ?") do |listy_thing| - aggregated = listy_thing.contents + @contents + result_iterator = self.deep_copy + aggregated = listy_thing.contents + result_iterator.contents unionized = aggregated.uniq {|element| element.inspect} - @contents = unionized + result_iterator.contents = unionized + result_iterator + end + end + + + duck_handle :again do + if index_in_range? + output_array = self.output_value + output_array.unshift(self) + else + reset_index_within_range + self + end + end + + + duck_handle :index= do + Closure.new(["neg"], "#{self.inspect}.index=?") do |int| + @index = int.value self end end @@ -119,9 +159,9 @@ def reset_index_within_range duck_handle :step do if index_in_range? - output = @contents.collect {|item| item.deep_copy} + output_array = self.output_value @index += @actual_inc - output.unshift(self) + output_array.unshift(self) else reset_index_within_range self diff --git a/lib/list_class.rb b/lib/list_class.rb index bc361ae..f05a48f 100644 --- a/lib/list_class.rb +++ b/lib/list_class.rb @@ -1,10 +1,10 @@ #encoding:utf-8 - -def list(contents=[]) - List.new(contents) -end - module Duck + def list(contents=[]) + List.new(contents) + end + + class List < Item attr_accessor :contents @@ -19,8 +19,12 @@ def deep_copy end - def emit!(msg) - reply = @contents.rindex {|item| item.recognize_message?(msg.intern)} + def emit!(key) + if key.class == String + reply = @contents.rindex {|item| item.recognize_message?(key.intern)} + elsif key.class == Class + reply = @contents.rindex {|item| item.kind_of?(key)} + end item = @contents.delete_at(reply) unless reply.nil? item end @@ -87,8 +91,13 @@ def to_s @contents << @contents[-1].deep_copy unless @contents.empty? self end - - + + + duck_handle :each do + Iterator.new(start:0, end:@contents.length, :response => :element, contents:@contents).run + end + + duck_handle :empty do @contents = [] self @@ -119,13 +128,15 @@ def to_s end - duck_handle :fold_up do - @contents.inject do |memo, item| + duck_handle :fold_down do + @contents.reverse.inject do |memo, item| case when memo.nil? item when memo.kind_of?(Array) - memo.collect {|branch| branch.grab(item)}.flatten.compact + memo.collect do |branch| + branch.nil? ? item : branch.grab(item) + end.flatten.compact else memo.grab(item) end @@ -133,13 +144,15 @@ def to_s end - duck_handle :fold_down do - @contents.reverse.inject do |memo, item| + duck_handle :fold_up do + @contents.inject do |memo, item| case when memo.nil? item when memo.kind_of?(Array) - memo.collect {|branch| branch.grab(item)}.flatten.compact + memo.collect do |branch| + branch.nil? ? item : branch.grab(item) + end.flatten.compact else memo.grab(item) end diff --git a/lib/local_class.rb b/lib/local_class.rb index e996a06..ac2fa1b 100644 --- a/lib/local_class.rb +++ b/lib/local_class.rb @@ -1,11 +1,13 @@ module Duck - def local(string) - Local.new(string) - end class Local < Message + def local(string) + Local.new(string) + end + + def initialize(string) raise ArgumentError, "Local cannot be made from \"#{string}\" without leading underscore" unless string[0] == "_" @@ -32,5 +34,11 @@ def to_s end end end + + + duck_handle :rand do + rand_chars = (@value.length-1).times.inject("") {|m,i| m+('a'..'z').to_a.sample} + Local.new("_#{rand_chars}") + end end end \ No newline at end of file diff --git a/lib/number_class.rb b/lib/number_class.rb index a88310b..d1f06e8 100644 --- a/lib/number_class.rb +++ b/lib/number_class.rb @@ -4,30 +4,35 @@ class Number < Item duck_handle :< do Closure.new(["neg"],"? < #{self.value}") {|arg1| Bool.new(arg1.value < self.value)} end - - + + duck_handle :≤ do Closure.new(["neg"],"? ≤ #{self.value}") {|arg1| Bool.new(arg1.value <= self.value)} end - - + + duck_handle :≥ do Closure.new(["neg"],"? ≥ #{self.value}") {|arg1| Bool.new(arg1.value >= self.value)} end - - + + duck_handle :> do Closure.new(["neg"],"? > #{self.value}") {|arg1| Bool.new(arg1.value > self.value)} end - - + + duck_handle :eql do Closure.new(["neg"],"? == #{self.value}") {|arg1| Bool.new(arg1.value == self.value)} end - - + + duck_handle :neg do self.class.new(-@value) end + + + duck_handle :to_script do + Script.new(@value.to_s) + end end end diff --git a/lib/pipe_class.rb b/lib/pipe_class.rb index 83c3d82..36886a0 100644 --- a/lib/pipe_class.rb +++ b/lib/pipe_class.rb @@ -15,7 +15,7 @@ def deep_copy Pipe.new(new_contents) end - define_method( "(".intern ) {List.new(@contents.clone)} + define_method( "(".intern ) {List.new(@contents)} def to_s "λ( " + (@contents.inject("(") {|s,i| s+i.to_s+", "}).chomp(", ") + ", ?) )" diff --git a/lib/proxy_class.rb b/lib/proxy_class.rb index b52dbb2..d9e46fb 100644 --- a/lib/proxy_class.rb +++ b/lib/proxy_class.rb @@ -28,6 +28,14 @@ def to_s end + duck_handle "^greedy=".intern do + Closure.new(["¬"], ".greedy=(?)") do |bool| + @value.greedy_flag = bool.value + nil + end + end + + duck_handle "^length".intern do @value.length end @@ -36,7 +44,10 @@ def to_s duck_handle "^quote".intern do Closure.new(["inc"], "