Class: Porolog::Rule

Inherits:
Object show all
Defined in:
lib/porolog/rule.rb

Overview

A Porolog::Rule is one clause of a Porolog::Predicate.

Author:

  • Luis Esteban

Defined Under Namespace

Classes: DefinitionError, Error

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(arguments, definition = nil) ⇒ Rule

Initializes the Rule.

Parameters:

  • arguments (Porolog::Arguments)

    the Arguments of the Predicate for which this Rule applies.

  • definition (Object) (defaults to: nil)

    the definition of the Rule.



40
41
42
43
44
# File 'lib/porolog/rule.rb', line 40

def initialize(arguments, definition = nil)
  @arguments  = arguments
  @definition = definition
  @@rules << self
end

Instance Attribute Details

#argumentsObject (readonly)

The Arguments of the Predicate for which this Rule applies.



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/porolog/rule.rb', line 19

class Rule
  
  # Error class for rescuing or detecting any Rule error.
  class Error           < PorologError ; end
  # Error class indicating that there is an error in the definition of the Rule.
  class DefinitionError < Error        ; end
  
  attr_reader :arguments, :definition
  
  # Clears all Rules.
  # @return [Boolean] success
  def self.reset
    @@rules = []
    true
  end
  
  reset
  
  # Initializes the Rule.
  # @param arguments [Porolog::Arguments] the Arguments of the Predicate for which this Rule applies.
  # @param definition [Object] the definition of the Rule.
  def initialize(arguments, definition = nil)
    @arguments  = arguments
    @definition = definition
    @@rules << self
  end
  
  # Convenience method for testing/debugging
  # @return [String] pretty identification
  def myid
    "Rule#{(@@rules.index(self) || -1000) + 1}"
  end
  
  # @return [String] pretty representation.
  def inspect
    "  #{@arguments.inspect}:- #{@definition.inspect}"
  end
  
  # Try to satisfy the Rule for the given Goal.
  # @param goal [Porolog::Goal] the Goal in which to satisfy this Rule.
  # @param block [Proc] code to perform when the Rule is satisfied.
  # @return [Boolean] the success of deleting the subgoal.
  def satisfy(goal, &block)
    subgoal = Goal.new self.arguments, goal
    
    unified_goals = unify_goals(goal, subgoal)
    if unified_goals
      satisfy_definition(goal, subgoal) do |solution_goal|
        block.call(solution_goal)
      end
    else
      subgoal.log << "Dead-end: Cannot unify with #{goal.inspect}"
    end
    
    subgoal.delete!
  end
  
  # Try to satisfy the definition of the Rule for a given Goal.
  # @param goal [Porolog::Goal] the given Goal for the Rule.
  # @param subgoal [Porolog::Goal] the subgoal for the Rule's definition.
  # @param block [Proc] code to perform when the definition is satisfied.
  # @return [Boolean] whether the definition was satisfied.
  def satisfy_definition(goal, subgoal, &block)
    case @definition
      when TrueClass
        block.call(subgoal)
        true
      
      when FalseClass
        false
      
      when Array
        satisfy_conjunction(goal, subgoal, @definition) do |solution_goal|
          block.call(solution_goal)
        end
      
      else
        raise DefinitionError, "UNEXPECTED TYPE OF DEFINITION: #{@definition.inspect} (#{@definition.class})"
    end
  end
  
  # Try to satisfy the conjunction of the definition of the Rule for a given Goal.
  # A conjunction is a sequence of expressions where the sequence is true
  # if all the expressions are true.
  #
  # @param goal [Porolog::Goal] the given Goal for the Rule.
  # @param subgoal [Porolog::Goal] the subgoal for the Rule's definition.
  # @param conjunction [Array] the conjunction to satisfy.
  # @param block [Proc] code to perform when the definition is satisfied.
  # @return [Boolean] whether the definition was satisfied.
  def satisfy_conjunction(goal, subgoal, conjunction, &block)
    arguments   = conjunction.first
    conjunction = conjunction[1..-1]
    
    # -- Handle non-Arguments --
    case arguments
      when :CUT, true
        subgoal.log << "CUTTING #{goal.inspect}..."
        result = true
        if conjunction.empty?
          !goal.terminated? && block.call(subgoal)
        else
          result = satisfy_conjunction(goal, subgoal, conjunction, &block)
        end
        
        if arguments == :CUT
          goal.terminate!
          goal.log << "TERMINATED after #{subgoal.inspect}"
        end
        
        return result
        
      when false
        return false
      
      when nil
        !goal.terminated? && block.call(subgoal)
        return true
    end
    
    # -- Unify Subsubgoal --
    subsubgoal = arguments.goal(subgoal)
    unified    = subsubgoal.inherit_variables(subgoal)
    
    # -- Satisfy Subgoal --
    result = false
    unified && subsubgoal.satisfy() do
      result = true
      if conjunction.empty?
        !goal.terminated? && block.call(goal)
      else
        result = !goal.terminated? && satisfy_conjunction(goal, subsubgoal, conjunction, &block)
      end
    end
    
    # -- Uninstantiate --
    subsubgoal.delete!
    
    result && !goal.terminated?
  end
  
end

#definitionObject (readonly)

The definition of the Rule.



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/porolog/rule.rb', line 19

class Rule
  
  # Error class for rescuing or detecting any Rule error.
  class Error           < PorologError ; end
  # Error class indicating that there is an error in the definition of the Rule.
  class DefinitionError < Error        ; end
  
  attr_reader :arguments, :definition
  
  # Clears all Rules.
  # @return [Boolean] success
  def self.reset
    @@rules = []
    true
  end
  
  reset
  
  # Initializes the Rule.
  # @param arguments [Porolog::Arguments] the Arguments of the Predicate for which this Rule applies.
  # @param definition [Object] the definition of the Rule.
  def initialize(arguments, definition = nil)
    @arguments  = arguments
    @definition = definition
    @@rules << self
  end
  
  # Convenience method for testing/debugging
  # @return [String] pretty identification
  def myid
    "Rule#{(@@rules.index(self) || -1000) + 1}"
  end
  
  # @return [String] pretty representation.
  def inspect
    "  #{@arguments.inspect}:- #{@definition.inspect}"
  end
  
  # Try to satisfy the Rule for the given Goal.
  # @param goal [Porolog::Goal] the Goal in which to satisfy this Rule.
  # @param block [Proc] code to perform when the Rule is satisfied.
  # @return [Boolean] the success of deleting the subgoal.
  def satisfy(goal, &block)
    subgoal = Goal.new self.arguments, goal
    
    unified_goals = unify_goals(goal, subgoal)
    if unified_goals
      satisfy_definition(goal, subgoal) do |solution_goal|
        block.call(solution_goal)
      end
    else
      subgoal.log << "Dead-end: Cannot unify with #{goal.inspect}"
    end
    
    subgoal.delete!
  end
  
  # Try to satisfy the definition of the Rule for a given Goal.
  # @param goal [Porolog::Goal] the given Goal for the Rule.
  # @param subgoal [Porolog::Goal] the subgoal for the Rule's definition.
  # @param block [Proc] code to perform when the definition is satisfied.
  # @return [Boolean] whether the definition was satisfied.
  def satisfy_definition(goal, subgoal, &block)
    case @definition
      when TrueClass
        block.call(subgoal)
        true
      
      when FalseClass
        false
      
      when Array
        satisfy_conjunction(goal, subgoal, @definition) do |solution_goal|
          block.call(solution_goal)
        end
      
      else
        raise DefinitionError, "UNEXPECTED TYPE OF DEFINITION: #{@definition.inspect} (#{@definition.class})"
    end
  end
  
  # Try to satisfy the conjunction of the definition of the Rule for a given Goal.
  # A conjunction is a sequence of expressions where the sequence is true
  # if all the expressions are true.
  #
  # @param goal [Porolog::Goal] the given Goal for the Rule.
  # @param subgoal [Porolog::Goal] the subgoal for the Rule's definition.
  # @param conjunction [Array] the conjunction to satisfy.
  # @param block [Proc] code to perform when the definition is satisfied.
  # @return [Boolean] whether the definition was satisfied.
  def satisfy_conjunction(goal, subgoal, conjunction, &block)
    arguments   = conjunction.first
    conjunction = conjunction[1..-1]
    
    # -- Handle non-Arguments --
    case arguments
      when :CUT, true
        subgoal.log << "CUTTING #{goal.inspect}..."
        result = true
        if conjunction.empty?
          !goal.terminated? && block.call(subgoal)
        else
          result = satisfy_conjunction(goal, subgoal, conjunction, &block)
        end
        
        if arguments == :CUT
          goal.terminate!
          goal.log << "TERMINATED after #{subgoal.inspect}"
        end
        
        return result
        
      when false
        return false
      
      when nil
        !goal.terminated? && block.call(subgoal)
        return true
    end
    
    # -- Unify Subsubgoal --
    subsubgoal = arguments.goal(subgoal)
    unified    = subsubgoal.inherit_variables(subgoal)
    
    # -- Satisfy Subgoal --
    result = false
    unified && subsubgoal.satisfy() do
      result = true
      if conjunction.empty?
        !goal.terminated? && block.call(goal)
      else
        result = !goal.terminated? && satisfy_conjunction(goal, subsubgoal, conjunction, &block)
      end
    end
    
    # -- Uninstantiate --
    subsubgoal.delete!
    
    result && !goal.terminated?
  end
  
end

Class Method Details

.resetBoolean

Clears all Rules.

Returns:

  • (Boolean)

    success



30
31
32
33
# File 'lib/porolog/rule.rb', line 30

def self.reset
  @@rules = []
  true
end

Instance Method Details

#inspectString

Returns pretty representation.

Returns:

  • (String)

    pretty representation.



53
54
55
# File 'lib/porolog/rule.rb', line 53

def inspect
  "  #{@arguments.inspect}:- #{@definition.inspect}"
end

#myidString

Convenience method for testing/debugging

Returns:

  • (String)

    pretty identification



48
49
50
# File 'lib/porolog/rule.rb', line 48

def myid
  "Rule#{(@@rules.index(self) || -1000) + 1}"
end

#satisfy(goal, &block) ⇒ Boolean

Try to satisfy the Rule for the given Goal.

Parameters:

  • goal (Porolog::Goal)

    the Goal in which to satisfy this Rule.

  • block (Proc)

    code to perform when the Rule is satisfied.

Returns:

  • (Boolean)

    the success of deleting the subgoal.



61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/porolog/rule.rb', line 61

def satisfy(goal, &block)
  subgoal = Goal.new self.arguments, goal
  
  unified_goals = unify_goals(goal, subgoal)
  if unified_goals
    satisfy_definition(goal, subgoal) do |solution_goal|
      block.call(solution_goal)
    end
  else
    subgoal.log << "Dead-end: Cannot unify with #{goal.inspect}"
  end
  
  subgoal.delete!
end

#satisfy_conjunction(goal, subgoal, conjunction, &block) ⇒ Boolean

Try to satisfy the conjunction of the definition of the Rule for a given Goal. A conjunction is a sequence of expressions where the sequence is true if all the expressions are true.

Parameters:

  • goal (Porolog::Goal)

    the given Goal for the Rule.

  • subgoal (Porolog::Goal)

    the subgoal for the Rule’s definition.

  • conjunction (Array)

    the conjunction to satisfy.

  • block (Proc)

    code to perform when the definition is satisfied.

Returns:

  • (Boolean)

    whether the definition was satisfied.



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/porolog/rule.rb', line 109

def satisfy_conjunction(goal, subgoal, conjunction, &block)
  arguments   = conjunction.first
  conjunction = conjunction[1..-1]
  
  # -- Handle non-Arguments --
  case arguments
    when :CUT, true
      subgoal.log << "CUTTING #{goal.inspect}..."
      result = true
      if conjunction.empty?
        !goal.terminated? && block.call(subgoal)
      else
        result = satisfy_conjunction(goal, subgoal, conjunction, &block)
      end
      
      if arguments == :CUT
        goal.terminate!
        goal.log << "TERMINATED after #{subgoal.inspect}"
      end
      
      return result
      
    when false
      return false
    
    when nil
      !goal.terminated? && block.call(subgoal)
      return true
  end
  
  # -- Unify Subsubgoal --
  subsubgoal = arguments.goal(subgoal)
  unified    = subsubgoal.inherit_variables(subgoal)
  
  # -- Satisfy Subgoal --
  result = false
  unified && subsubgoal.satisfy() do
    result = true
    if conjunction.empty?
      !goal.terminated? && block.call(goal)
    else
      result = !goal.terminated? && satisfy_conjunction(goal, subsubgoal, conjunction, &block)
    end
  end
  
  # -- Uninstantiate --
  subsubgoal.delete!
  
  result && !goal.terminated?
end

#satisfy_definition(goal, subgoal, &block) ⇒ Boolean

Try to satisfy the definition of the Rule for a given Goal.

Parameters:

  • goal (Porolog::Goal)

    the given Goal for the Rule.

  • subgoal (Porolog::Goal)

    the subgoal for the Rule’s definition.

  • block (Proc)

    code to perform when the definition is satisfied.

Returns:

  • (Boolean)

    whether the definition was satisfied.



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/porolog/rule.rb', line 81

def satisfy_definition(goal, subgoal, &block)
  case @definition
    when TrueClass
      block.call(subgoal)
      true
    
    when FalseClass
      false
    
    when Array
      satisfy_conjunction(goal, subgoal, @definition) do |solution_goal|
        block.call(solution_goal)
      end
    
    else
      raise DefinitionError, "UNEXPECTED TYPE OF DEFINITION: #{@definition.inspect} (#{@definition.class})"
  end
end