Permalink
Browse files

added alpha memory node to reduce computation for shared nodes

  • Loading branch information...
jkutner committed Jun 30, 2011
1 parent 01e888a commit d704b36d9ced9831e36519636b9c97c98ceb5c37
Showing with 88 additions and 30 deletions.
  1. +67 −27 lib/core/nodes.rb
  2. +21 −3 spec/function_spec.rb
View
@@ -459,34 +459,67 @@ def assert(fact)
end
end
end
class AlphaMemoryNode < AtomNode
def initialize(bucket, atom)
super(bucket, atom)
@memory = Hash.new(DEFAULT)
end
def retract(assertable)
forget(assertable)
super
end
def remember(assertable)
@memory[assertable.fact.object_id]
end
def memorized?(assertable)
@memory[assertable.fact.object_id] != DEFAULT
end
def memorize(assertable, value)
@memory[assertable.fact.object_id] = value
end
def forget(assertable)
@memory.delete(assertable.fact.object_id)
end
end
# This node class is used for matching properties of a fact.
class PropertyNode < AtomNode
class PropertyNode < AlphaMemoryNode
def assert(assertable)
begin
v = assertable.fact.object.send(@atom.slot)
assertable.add_tag(@atom.tag, v)
rescue NoMethodError => e
@bucket.add_error Error.new(:no_method, :warn, {
:object => assertable.fact.object.to_s,
:method => e.name,
:message => e.message
})
return
end
begin
if @atom.proc.arity == 1
super if @atom.proc.call(v)
else
super if @atom.proc.call(v, @atom.value)
unless memorized?(assertable)
begin
v = assertable.fact.object.send(@atom.slot)
assertable.add_tag(@atom.tag, v)
begin
if @atom.proc.arity == 1
r = @atom.proc.call(v)
else
r = @atom.proc.call(v, @atom.value)
end
memorize(assertable, r)
super if r
rescue Exception => e
@bucket.add_error Error.new(:proc_call, :error, {
:object => assertable.fact.object.to_s,
:method => @atom.slot,
:value => v.to_s,
:message => e.message
})
end
rescue NoMethodError => e
@bucket.add_error Error.new(:no_method, :warn, {
:object => assertable.fact.object.to_s,
:method => e.name,
:message => e.message
})
end
rescue Exception => e
@bucket.add_error Error.new(:proc_call, :error, {
:object => assertable.fact.object.to_s,
:method => @atom.slot,
:value => v.to_s,
:message => e.message
})
else
super if remember(assertable)
end
end
end
@@ -501,10 +534,13 @@ def hash_by(atom)
end
# This node class is used conditions that are simply a function, which returns true or false.
class FunctionNode < AtomNode
class FunctionNode < AlphaMemoryNode
def assert(assertable)
begin
super if @atom.proc.call(assertable.fact.object, *@atom.arguments)
unless memorized?(assertable)
memorize(assertable, @atom.proc.call(assertable.fact.object, *@atom.arguments))
end
super if remember(assertable)
rescue Exception => e
@bucket.add_error Error.new(:proc_call, :error, {
:object => fact.object.to_s,
@@ -546,7 +582,7 @@ def match(left_context,right_fact)
:message => e.message
})
end
return MatchResult.new
MatchResult.new
end
end
@@ -1001,5 +1037,9 @@ def tags
end
end
class MemoryDefault
end
DEFAULT = MemoryDefault.new
end
end
View
@@ -24,11 +24,11 @@ def rules_with_function_testing_self(arg)
def rules_that_share_a_function
func = c{|a, b| a.times += 1; b == "foobar"}
rule [FuncFact, :a, f("foobar", func)] do |v|
rule [FuncFact, :a, m.value > 1, f("foobar", func)] do |v|
assert Success.new
end
rule [FuncFact, :a, f("foobar", func)] do |v|
rule [FuncFact, :a, m.value > 2, f("foobar", func)] do |v|
assert Success.new
end
end
@@ -121,7 +121,7 @@ def rules_with_no_args_function
context "with one FuncFact" do
before do
@f = FuncFact.new
@f = FuncFact.new(3)
subject.assert @f
subject.match
end
@@ -132,6 +132,24 @@ def rules_with_no_args_function
subject.errors.should == []
@f.times.should == 1
subject.retract r[0]
subject.retract r[1]
subject.retract @f
subject.match
r = subject.retrieve Success
r.size.should == 0
subject.errors.should == []
subject.assert @f
subject.match
r = subject.retrieve Success
r.size.should == 2
subject.errors.should == []
@f.times.should == 2
end
end
end

0 comments on commit d704b36

Please sign in to comment.