Permalink
Browse files

added alpha memory node to reduce computation for shared nodes

  • Loading branch information...
1 parent 01e888a commit d704b36d9ced9831e36519636b9c97c98ceb5c37 @jkutner jkutner committed Jun 30, 2011
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.