porolog 0.0.7 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +18 -4
- data/bin/porolog +38 -2
- data/coverage/badge.svg +1 -1
- data/coverage/index.html +52327 -6692
- data/doc/Array.html +113 -33
- data/doc/Object.html +11 -17
- data/doc/Porolog.html +3681 -73
- data/doc/Symbol.html +17 -20
- data/doc/_index.html +181 -13
- data/doc/class_list.html +1 -1
- data/doc/file.README.html +28 -38
- data/doc/index.html +28 -38
- data/doc/method_list.html +871 -167
- data/doc/top-level-namespace.html +2 -2
- data/lib/porolog.rb +1015 -2
- data/lib/porolog/arguments.rb +16 -14
- data/lib/porolog/core_ext.rb +6 -0
- data/lib/porolog/error.rb +6 -0
- data/lib/porolog/goal.rb +205 -68
- data/lib/porolog/instantiation.rb +300 -0
- data/lib/porolog/predicate.rb +33 -16
- data/lib/porolog/rule.rb +129 -2
- data/lib/porolog/scope.rb +3 -3
- data/lib/porolog/tail.rb +52 -0
- data/lib/porolog/value.rb +9 -12
- data/lib/porolog/variable.rb +307 -0
- data/test/porolog/arguments_test.rb +217 -135
- data/test/porolog/core_ext_test.rb +24 -17
- data/test/porolog/goal_test.rb +481 -74
- data/test/porolog/instantiation_test.rb +874 -0
- data/test/porolog/porolog_test.rb +2121 -13
- data/test/porolog/predicate_test.rb +1 -1
- data/test/porolog/rule_test.rb +395 -0
- data/test/porolog/scope_test.rb +0 -2
- data/test/porolog/tail_test.rb +127 -0
- data/test/porolog/value_test.rb +1 -1
- data/test/porolog/variable_test.rb +1625 -0
- data/test/test_helper.rb +78 -5
- metadata +12 -4
data/lib/porolog/scope.rb
CHANGED
@@ -16,9 +16,9 @@ module Porolog
|
|
16
16
|
class Scope
|
17
17
|
|
18
18
|
# Error class for rescuing or detecting any Scope error.
|
19
|
-
class
|
19
|
+
class Error < PorologError ; end
|
20
20
|
# Error class indicating a non-Predicate was assigned to a Scope.
|
21
|
-
class NotPredicateError <
|
21
|
+
class NotPredicateError < Error ; end
|
22
22
|
|
23
23
|
attr_reader :name
|
24
24
|
|
@@ -74,7 +74,7 @@ module Porolog
|
|
74
74
|
# @return [Porolog::Predicate] the Predicate assigned to the Scope.
|
75
75
|
# @raise [NotPredicateError] when provided predicate is not actually a Predicate.
|
76
76
|
def []=(name,predicate)
|
77
|
-
raise NotPredicateError
|
77
|
+
raise NotPredicateError, "#{predicate.inspect} is not a Predicate" unless predicate.is_a?(Predicate)
|
78
78
|
@predicates[name.to_sym] = predicate
|
79
79
|
end
|
80
80
|
|
data/lib/porolog/tail.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
#
|
2
|
+
# lib/porolog/tail.rb - Plain Old Ruby Objects Prolog Engine -- Tail
|
3
|
+
#
|
4
|
+
# Luis Esteban 2 May 2018
|
5
|
+
# created
|
6
|
+
#
|
7
|
+
|
8
|
+
module Porolog
|
9
|
+
|
10
|
+
# A Porolog::Tail is used to represent the tail of a list.
|
11
|
+
#
|
12
|
+
# It corresponds to the use of the splat operator within an Array.
|
13
|
+
#
|
14
|
+
# @author Luis Esteban
|
15
|
+
#
|
16
|
+
# @!attribute value
|
17
|
+
# @return [Object] The value of the tail.
|
18
|
+
class Tail
|
19
|
+
|
20
|
+
# Creates a new Tail for an Array.
|
21
|
+
# @param value [Object] the value of the tail.
|
22
|
+
def initialize(value = UNKNOWN_TAIL)
|
23
|
+
@value = value
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns the value of the Tail.
|
27
|
+
# The optional arguments are ignored; this is for polymorphic compatibility with Porolog::Value and Porolog::Variable,
|
28
|
+
# which are used to prevent inifinite recursion.
|
29
|
+
# @return [Object] the value of the Tail.
|
30
|
+
def value(*)
|
31
|
+
@value
|
32
|
+
end
|
33
|
+
|
34
|
+
# @return [String] pretty representation.
|
35
|
+
def inspect
|
36
|
+
"*#{@value.inspect}"
|
37
|
+
end
|
38
|
+
|
39
|
+
# @return [Array] embedded variables.
|
40
|
+
def variables
|
41
|
+
@value.variables
|
42
|
+
end
|
43
|
+
|
44
|
+
# @param other [Object, #value]
|
45
|
+
# @return [Boolean] whether the value of the Tail is equal to the value of another Object.
|
46
|
+
def ==(other)
|
47
|
+
@value == other.value
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
data/lib/porolog/value.rb
CHANGED
@@ -7,22 +7,22 @@
|
|
7
7
|
|
8
8
|
module Porolog
|
9
9
|
|
10
|
-
# Porolog::Value
|
11
|
-
#
|
12
|
-
# A Porolog::Value combines a value with a goal so that when the goal
|
13
|
-
# is closed, the value can be uninstantiated at the same time.
|
10
|
+
# A Porolog::Value combines a value with a goal so that when the goal
|
11
|
+
# is closed, the value can be uninstantiated at the same time.
|
14
12
|
#
|
15
13
|
# @author Luis Esteban
|
14
|
+
#
|
16
15
|
# @!attribute goal
|
17
16
|
# @return [Porolog::Goal] The goal in which the value was instantiated.
|
18
17
|
# @!attribute instantiations
|
19
18
|
# @return [Array<Porolog::Instantiation>] Instantiations of this value.
|
19
|
+
#
|
20
20
|
class Value
|
21
21
|
|
22
22
|
# Error class for rescuing or detecting any Value error.
|
23
|
-
class Error
|
23
|
+
class Error < PorologError ; end
|
24
24
|
# Error class indicating that the supplied goal is not actually a goal.
|
25
|
-
class GoalError < Error
|
25
|
+
class GoalError < Error ; end
|
26
26
|
|
27
27
|
attr_accessor :goal, :instantiations
|
28
28
|
|
@@ -30,13 +30,10 @@ module Porolog
|
|
30
30
|
# @param goal [Porolog::Goal] the Goal to be associated.
|
31
31
|
# @return [Porolog::Value] the Value.
|
32
32
|
def initialize(value, goal)
|
33
|
-
raise GoalError
|
33
|
+
raise GoalError, "Not a Goal: #{goal.inspect}" unless goal.is_a?(Goal)
|
34
34
|
|
35
|
-
|
36
|
-
|
37
|
-
else
|
38
|
-
@value = value
|
39
|
-
end
|
35
|
+
@value = value
|
36
|
+
@value = value.value if value.is_a?(Value)
|
40
37
|
@goal = goal
|
41
38
|
|
42
39
|
@instantiations = []
|
@@ -0,0 +1,307 @@
|
|
1
|
+
#
|
2
|
+
# lib/porolog/variable.rb - Plain Old Ruby Objects Prolog Engine -- Variable
|
3
|
+
#
|
4
|
+
# Luis Esteban 2 May 2018
|
5
|
+
# created
|
6
|
+
#
|
7
|
+
|
8
|
+
|
9
|
+
module Porolog
|
10
|
+
|
11
|
+
# A Porolog::Variable is used to hold instantiations during the process of satisfying a goal.
|
12
|
+
# It implements a variable of a goal.
|
13
|
+
# It allows instantiations to be made and removed as the goal
|
14
|
+
# is attempted to be satisfied.
|
15
|
+
#
|
16
|
+
# @author Luis Esteban
|
17
|
+
#
|
18
|
+
# @!attribute name
|
19
|
+
# @return [Symbol] The name of the variable.
|
20
|
+
# @!attribute goal
|
21
|
+
# @return [Porolog::Goal] The Goal for which this Variable's instantiations are bound.
|
22
|
+
# @!attribute instantiations
|
23
|
+
# @return [Array<Porolog::Instantiation>] The Instantiations of this Variable.
|
24
|
+
# @!attribute values
|
25
|
+
# @return [Array<Porolog::Value>] the Values of the Variable when used as an anonymous Variable.
|
26
|
+
#
|
27
|
+
class Variable
|
28
|
+
|
29
|
+
# Error class for rescuing or detecting any Variable error.
|
30
|
+
class Error < PorologError ; end
|
31
|
+
# Error class indicating a Variable has been instantiated to multiple different values at the same time.
|
32
|
+
class MultipleValuesError < Error ; end
|
33
|
+
# Error class indicating a Variable has been created without a Goal.
|
34
|
+
class GoalError < Error ; end
|
35
|
+
# Error class indicating a Variable has been instantiated to a value that contains itself.
|
36
|
+
class SelfReferentialError < Error ; end
|
37
|
+
# Error class indicating an unexpected scenario has occurred.
|
38
|
+
class UnexpectedError < Error ; end
|
39
|
+
|
40
|
+
attr_accessor :name, :goal, :instantiations, :values
|
41
|
+
|
42
|
+
# Initializes the Variable and attaches it to the Goal.
|
43
|
+
# @param name [Object] the name used to refer to the Variable.
|
44
|
+
# @param goal [Porolog::Goal] the Goal the Variable is to be attached to.
|
45
|
+
def initialize(name, goal)
|
46
|
+
raise GoalError, "Not a Goal: #{goal.inspect}" unless goal.is_a?(Goal)
|
47
|
+
@goal = goal
|
48
|
+
name = name.to_sym if name.is_a?(String)
|
49
|
+
|
50
|
+
case name
|
51
|
+
when Symbol
|
52
|
+
@name = name
|
53
|
+
@values = []
|
54
|
+
when Variable
|
55
|
+
@name = name.name
|
56
|
+
@values = []
|
57
|
+
when Value
|
58
|
+
@name = name.value
|
59
|
+
@values = [name]
|
60
|
+
else
|
61
|
+
@name = name.to_s
|
62
|
+
@values = [Value.new(name, goal)]
|
63
|
+
end
|
64
|
+
|
65
|
+
@instantiations = []
|
66
|
+
@goal.variable(self)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Converts a Variable back to a Symbol.
|
70
|
+
# @return [Symbol, nil] the name of the Variable.
|
71
|
+
def to_sym
|
72
|
+
@name && @name.to_sym
|
73
|
+
end
|
74
|
+
|
75
|
+
# @return [Symbol] the type of the Variable, which should be :variable.
|
76
|
+
def type
|
77
|
+
:variable
|
78
|
+
end
|
79
|
+
|
80
|
+
# @return [String] pretty representation.
|
81
|
+
def inspect
|
82
|
+
"#{@goal.myid}.#{@name.inspect}"
|
83
|
+
end
|
84
|
+
|
85
|
+
# @param visited [Array] used to prevent infinite recursion.
|
86
|
+
# @param depth [Integer] the current level of indentation.
|
87
|
+
# @param index [Object, nil] the index into this Variable that is instantiated.
|
88
|
+
# @param self_index [Object, nil] the index where this Variable is instantiated.
|
89
|
+
# @return [String] the inspect of the Variable showing instantiations using indentation.
|
90
|
+
def inspect_with_instantiations(visited = [], depth = 0, index = nil, self_index = nil)
|
91
|
+
return if visited.include?(self)
|
92
|
+
|
93
|
+
index_str = index && "[#{index.inspect}]" || ''
|
94
|
+
prefix = self_index && "[#{self_index.inspect}]" || ''
|
95
|
+
name = "#{' ' * depth}#{prefix}#{@goal.myid}.#{@name.inspect}#{index_str}"
|
96
|
+
|
97
|
+
name = "#{name} = #{@values.map(&:inspect).join(',')}#{index_str}" if @values && !@values.empty? && @instantiations.empty?
|
98
|
+
|
99
|
+
others = @instantiations.map{|instantiation|
|
100
|
+
[
|
101
|
+
instantiation.variable1.inspect_with_instantiations(visited + [self], depth + 1, instantiation.index1, instantiation.index2),
|
102
|
+
instantiation.variable2.inspect_with_instantiations(visited + [self], depth + 1, instantiation.index2, instantiation.index1),
|
103
|
+
].compact
|
104
|
+
}.reject(&:empty?)
|
105
|
+
|
106
|
+
[
|
107
|
+
name,
|
108
|
+
*others,
|
109
|
+
].join("\n")
|
110
|
+
end
|
111
|
+
|
112
|
+
# @return [Object,self] returns the current value of the Variable based on its current instantiations.
|
113
|
+
# If there are no concrete instantiations, it returns itself, indicating no value.
|
114
|
+
# @param visited [Array] prevents infinite recursion.
|
115
|
+
def value(visited = [])
|
116
|
+
return nil if visited.include?(self)
|
117
|
+
visited = visited + [self]
|
118
|
+
|
119
|
+
# -- Collect values --
|
120
|
+
values = [*@values]
|
121
|
+
|
122
|
+
@instantiations.each do |instantiation|
|
123
|
+
values += instantiation.values_for(self, visited)
|
124
|
+
end
|
125
|
+
|
126
|
+
values.uniq!
|
127
|
+
|
128
|
+
# -- Filter trivial values --
|
129
|
+
values = [[nil]] if values == [[UNKNOWN_TAIL], [nil]] || values == [[nil], [UNKNOWN_TAIL]]
|
130
|
+
|
131
|
+
values = values.reject{|value| value == UNKNOWN_TAIL }
|
132
|
+
|
133
|
+
values_values = values.map{|value| value.value(visited) }.reject{|value| value == UNKNOWN_ARRAY }
|
134
|
+
|
135
|
+
# -- Condense Values --
|
136
|
+
result = if values_values.size > 1
|
137
|
+
# -- Potentially Multiple Values Found --
|
138
|
+
if values_values.all?{|value| value.is_a?(Array) }
|
139
|
+
# -- All Values Are Arrays --
|
140
|
+
unifications = []
|
141
|
+
|
142
|
+
values_goals = values.map{|value|
|
143
|
+
value.respond_to?(:goal) && value.goal || value.variables.map(&:goal).first
|
144
|
+
}
|
145
|
+
|
146
|
+
merged, unifications = unify_many_arrays(values, values_goals, visited)
|
147
|
+
|
148
|
+
merged.value(visited).to_a
|
149
|
+
else
|
150
|
+
# -- Not All Values Are Arrays --
|
151
|
+
unifications = []
|
152
|
+
values.each_cons(2){|left,right|
|
153
|
+
unification = unify(left, right, @goal, @goal, visited)
|
154
|
+
if unification && unifications
|
155
|
+
unifications += unification
|
156
|
+
else
|
157
|
+
unifications = nil
|
158
|
+
end
|
159
|
+
}
|
160
|
+
if unifications
|
161
|
+
values.sort_by{|value|
|
162
|
+
case value
|
163
|
+
when Variable, Symbol then 2
|
164
|
+
when UNKNOWN_TAIL, UNKNOWN_ARRAY then 9
|
165
|
+
else 0
|
166
|
+
end
|
167
|
+
}.first || self
|
168
|
+
else
|
169
|
+
raise MultipleValuesError, "Multiple values detected for #{inspect}: #{values.inspect}"
|
170
|
+
end
|
171
|
+
end
|
172
|
+
else
|
173
|
+
# -- One (or None) Value Found --
|
174
|
+
values.sort_by{|value|
|
175
|
+
case value
|
176
|
+
when Variable, Symbol then 2
|
177
|
+
when UNKNOWN_TAIL, UNKNOWN_ARRAY then 9
|
178
|
+
else 0
|
179
|
+
end
|
180
|
+
}.first || self
|
181
|
+
end
|
182
|
+
|
183
|
+
# -- Splat Tail --
|
184
|
+
if result.is_a?(Array) && result.size == 1 && result.first.is_a?(Tail)
|
185
|
+
result = result.first.value
|
186
|
+
end
|
187
|
+
|
188
|
+
result
|
189
|
+
end
|
190
|
+
|
191
|
+
# Instantiates Variable to another experssion.
|
192
|
+
# @param other [Object] the other value (or object) being instantiated.
|
193
|
+
# @param index_into_other [] the index into the other value.
|
194
|
+
# @param index_into_self [] the index into this Variable.
|
195
|
+
# @return [Porolog::Instantiation,nil] the instantiation made.
|
196
|
+
# @example
|
197
|
+
# # To instantiate the third element of x to the second element of y,
|
198
|
+
# # x = [a,b,c,d,e]
|
199
|
+
# # |
|
200
|
+
# # y = [p,q,r,s]
|
201
|
+
# x = goal.variable(:x)
|
202
|
+
# y = goal.variable(:y)
|
203
|
+
# x.instantiate(y, 1, 2)
|
204
|
+
# @example
|
205
|
+
# # To instantiate the tail of x to y,
|
206
|
+
# x.instantiate(y, nil, :tail)
|
207
|
+
def instantiate(other, index_into_other = nil, index_into_self = nil)
|
208
|
+
raise SelfReferentialError, "Cannot instantiate self-referential definition for #{(self.variables & other.variables).inspect}" unless (self.variables & other.variables).empty?
|
209
|
+
|
210
|
+
# -- Check Instantiation is Unifiable --
|
211
|
+
unless self.value.is_a?(Variable) || other.value.is_a?(Variable)
|
212
|
+
# -- Determine Other Goal --
|
213
|
+
other_goal = nil
|
214
|
+
other_goal = other.goal if other.respond_to?(:goal)
|
215
|
+
other_goal ||= self.goal
|
216
|
+
|
217
|
+
# -- Check Unification --
|
218
|
+
unless unify(self, other, self.goal, other_goal)
|
219
|
+
self.goal.log << "Cannot unify: #{self.inspect} and #{other.inspect}"
|
220
|
+
return nil
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
# -- Create Instantiation --
|
225
|
+
instantiation = Instantiation.new(self, index_into_self, other, index_into_other)
|
226
|
+
|
227
|
+
# -- Create Reverse Assymetric Instantiations --
|
228
|
+
if other.value.is_a?(Array) && other.value.last.is_a?(Tail)
|
229
|
+
array = other.value
|
230
|
+
if array.length == 2
|
231
|
+
if array.first.is_a?(Variable)
|
232
|
+
Instantiation.new(array.first, nil, self, :head)
|
233
|
+
end
|
234
|
+
if array.last.is_a?(Tail) && array.last.value.is_a?(Variable)
|
235
|
+
Instantiation.new(array.last.value, nil, self, :tail)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
# -- Return --
|
241
|
+
instantiation
|
242
|
+
end
|
243
|
+
|
244
|
+
# Removes this Variable by removing all of its insantiations.
|
245
|
+
# @return [Boolean] success.
|
246
|
+
def remove
|
247
|
+
@instantiations.dup.each(&:remove)
|
248
|
+
@instantiations[0..-1] = []
|
249
|
+
true
|
250
|
+
end
|
251
|
+
|
252
|
+
# Removes instantiations from another goal.
|
253
|
+
# @param other_goal [Porolog::Goal] the Goal of which instantiations are to be removed.
|
254
|
+
# @return [Boolean] success.
|
255
|
+
def uninstantiate(other_goal)
|
256
|
+
@instantiations.delete_if do |instantiation|
|
257
|
+
instantiation.remove if instantiation.other_goal_to(self) == other_goal
|
258
|
+
end
|
259
|
+
|
260
|
+
@values.delete_if{|value| value.goal == other_goal }
|
261
|
+
true
|
262
|
+
end
|
263
|
+
|
264
|
+
# Indexes the value of the Variable.
|
265
|
+
# @param index [Object] the index into the value.
|
266
|
+
# @return [Object, nil] the value at the index in the value of the Variable.
|
267
|
+
def [](index)
|
268
|
+
value = self.value
|
269
|
+
value = value.value if value.is_a?(Value)
|
270
|
+
|
271
|
+
case value
|
272
|
+
when Array
|
273
|
+
case index
|
274
|
+
when Integer
|
275
|
+
value[index]
|
276
|
+
when Symbol
|
277
|
+
case index
|
278
|
+
when :head
|
279
|
+
value[0]
|
280
|
+
when :tail
|
281
|
+
value[1..-1]
|
282
|
+
else
|
283
|
+
nil
|
284
|
+
end
|
285
|
+
else
|
286
|
+
nil
|
287
|
+
end
|
288
|
+
else
|
289
|
+
nil
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
# @return [Array<Porolog::Variable>] the Variables in itself, which is just itself.
|
294
|
+
def variables
|
295
|
+
[self]
|
296
|
+
end
|
297
|
+
|
298
|
+
# Compares Variables.
|
299
|
+
# @param other [Porolog::Variable] the other Variable.
|
300
|
+
# @return [Boolean] whether the two Variables have the same name in the same Goal.
|
301
|
+
def ==(other)
|
302
|
+
other.is_a?(Variable) && @name == other.name && @goal == other.goal
|
303
|
+
end
|
304
|
+
|
305
|
+
end
|
306
|
+
|
307
|
+
end
|
@@ -13,16 +13,19 @@ describe 'Porolog' do
|
|
13
13
|
reset
|
14
14
|
end
|
15
15
|
|
16
|
+
let(:pred_name) { :pred }
|
17
|
+
let(:pred) { Predicate.new pred_name }
|
18
|
+
let(:pred1) { Predicate.new :p }
|
19
|
+
let(:pred2) { Predicate.new :q }
|
20
|
+
|
16
21
|
describe 'Arguments' do
|
17
22
|
|
18
23
|
describe '.reset' do
|
19
24
|
|
20
25
|
it 'should delete/unregister all Arguments' do
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
args2 = Arguments.new p, [2]
|
25
|
-
args3 = Arguments.new p, [3,:x,:y,:z]
|
26
|
+
args1 = Arguments.new pred, [1,:x,'word',[2,:y,0.3]]
|
27
|
+
args2 = Arguments.new pred, [2]
|
28
|
+
args3 = Arguments.new pred, [3,:x,:y,:z]
|
26
29
|
|
27
30
|
assert_equal 'Arguments1', args1.myid
|
28
31
|
assert_equal 'Arguments2', args2.myid
|
@@ -30,8 +33,8 @@ describe 'Porolog' do
|
|
30
33
|
|
31
34
|
Arguments.reset
|
32
35
|
|
33
|
-
args4 = Arguments.new
|
34
|
-
args5 = Arguments.new
|
36
|
+
args4 = Arguments.new pred, [4,[1,2,3]]
|
37
|
+
args5 = Arguments.new pred, [5,[]]
|
35
38
|
|
36
39
|
assert_equal 'Arguments-999', args1.myid
|
37
40
|
assert_equal 'Arguments-999', args2.myid
|
@@ -53,10 +56,9 @@ describe 'Porolog' do
|
|
53
56
|
describe '.new' do
|
54
57
|
|
55
58
|
it 'should create a new Arguments' do
|
56
|
-
|
57
|
-
arguments = Arguments.new predicate, [1,:x,'word',[2,:y,0.3]]
|
59
|
+
arguments = Arguments.new pred, [1, :x, 'word', [2, :y, 0.3]]
|
58
60
|
|
59
|
-
assert_Arguments
|
61
|
+
assert_Arguments arguments, :pred, [1, :x, 'word', [2, :y, 0.3]]
|
60
62
|
end
|
61
63
|
|
62
64
|
end
|
@@ -64,21 +66,23 @@ describe 'Porolog' do
|
|
64
66
|
describe '.arguments' do
|
65
67
|
|
66
68
|
it 'should return all registered arguments' do
|
67
|
-
|
69
|
+
# -- No Arguments --
|
70
|
+
assert_equal 0, Arguments.arguments.size
|
68
71
|
|
69
|
-
|
70
|
-
arguments1 = Arguments.new
|
72
|
+
# -- One Arguments --
|
73
|
+
arguments1 = Arguments.new pred1, [:x,:y,:z]
|
71
74
|
|
72
|
-
assert_equal 1,
|
75
|
+
assert_equal 1, Arguments.arguments.size
|
73
76
|
assert_Arguments Arguments.arguments.last, :p, [:x,:y,:z]
|
74
77
|
|
75
|
-
|
76
|
-
arguments2 = Arguments.new
|
78
|
+
# -- Two Arguments --
|
79
|
+
arguments2 = Arguments.new pred2, [:a,:b,:c,:d]
|
77
80
|
|
78
|
-
assert_equal 2,
|
81
|
+
assert_equal 2, Arguments.arguments.size
|
79
82
|
assert_Arguments Arguments.arguments.last, :q, [:a,:b,:c,:d]
|
80
83
|
|
81
|
-
|
84
|
+
|
85
|
+
assert_equal [arguments1, arguments2], Arguments.arguments
|
82
86
|
end
|
83
87
|
|
84
88
|
end
|
@@ -86,18 +90,16 @@ describe 'Porolog' do
|
|
86
90
|
describe '#initialize' do
|
87
91
|
|
88
92
|
it 'should initialize predicate and arguments' do
|
89
|
-
|
90
|
-
arguments = Arguments.new predicate, [:x,:y,:z]
|
93
|
+
arguments = Arguments.new pred, [:x,:y,:z]
|
91
94
|
|
92
|
-
assert_equal
|
95
|
+
assert_equal pred, arguments.predicate
|
93
96
|
assert_equal [:x,:y,:z], arguments.arguments
|
94
97
|
end
|
95
98
|
|
96
99
|
it 'should register the arguments' do
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
arguments3 = Arguments.new predicate, [:x,:y,:z]
|
100
|
+
arguments1 = Arguments.new pred, [:x]
|
101
|
+
arguments2 = Arguments.new pred, [:x,:y]
|
102
|
+
arguments3 = Arguments.new pred, [:x,:y,:z]
|
101
103
|
|
102
104
|
assert_equal [
|
103
105
|
arguments1,
|
@@ -111,10 +113,9 @@ describe 'Porolog' do
|
|
111
113
|
describe '#myid' do
|
112
114
|
|
113
115
|
it 'should return its class and index as a String' do
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
arguments3 = Arguments.new predicate, [:x,:y,:z]
|
116
|
+
arguments1 = Arguments.new pred, [:x]
|
117
|
+
arguments2 = Arguments.new pred, [:x,:y]
|
118
|
+
arguments3 = Arguments.new pred, [:x,:y,:z]
|
118
119
|
|
119
120
|
assert_equal 'Arguments1', arguments1.myid
|
120
121
|
assert_equal 'Arguments2', arguments2.myid
|
@@ -122,10 +123,9 @@ describe 'Porolog' do
|
|
122
123
|
end
|
123
124
|
|
124
125
|
it 'should return its class and -999 as a String when deleted/reset' do
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
arguments3 = Arguments.new predicate, [:x,:y,:z]
|
126
|
+
arguments1 = Arguments.new pred, [:x]
|
127
|
+
arguments2 = Arguments.new pred, [:x,:y]
|
128
|
+
arguments3 = Arguments.new pred, [:x,:y,:z]
|
129
129
|
|
130
130
|
Arguments.reset
|
131
131
|
|
@@ -142,6 +142,7 @@ describe 'Porolog' do
|
|
142
142
|
predicate1 = Predicate.new :p
|
143
143
|
predicate2 = Predicate.new :q
|
144
144
|
predicate3 = Predicate.new :generic
|
145
|
+
|
145
146
|
arguments1 = Arguments.new predicate1, [:x]
|
146
147
|
arguments2 = Arguments.new predicate2, [:list, [1,2,3]]
|
147
148
|
arguments3 = Arguments.new predicate3, [:a,:b,:c]
|
@@ -156,12 +157,11 @@ describe 'Porolog' do
|
|
156
157
|
describe '#fact!' do
|
157
158
|
|
158
159
|
it 'should create a fact for its predicate' do
|
159
|
-
|
160
|
-
arguments1 = predicate1.(1,'a',0.1)
|
160
|
+
arguments1 = pred.(1,'a',0.1)
|
161
161
|
|
162
162
|
arguments1.fact!
|
163
163
|
|
164
|
-
assert_equal '[
|
164
|
+
assert_equal '[ pred(1,"a",0.1):- true]', pred.rules.inspect
|
165
165
|
end
|
166
166
|
|
167
167
|
end
|
@@ -169,12 +169,11 @@ describe 'Porolog' do
|
|
169
169
|
describe '#falicy!' do
|
170
170
|
|
171
171
|
it 'should create a falicy for its predicate' do
|
172
|
-
|
173
|
-
arguments1 = predicate1.(1,'a',0.1)
|
172
|
+
arguments1 = pred.(1,'a',0.1)
|
174
173
|
|
175
174
|
arguments1.falicy!
|
176
175
|
|
177
|
-
assert_equal '[
|
176
|
+
assert_equal '[ pred(1,"a",0.1):- false]', pred.rules.inspect
|
178
177
|
end
|
179
178
|
|
180
179
|
end
|
@@ -182,12 +181,11 @@ describe 'Porolog' do
|
|
182
181
|
describe '#cut_fact!' do
|
183
182
|
|
184
183
|
it 'should create a fact for its predicate and terminate solving the goal' do
|
185
|
-
|
186
|
-
arguments1 = predicate1.(1,'a',0.1)
|
184
|
+
arguments1 = pred.(1,'a',0.1)
|
187
185
|
|
188
186
|
arguments1.cut_fact!
|
189
187
|
|
190
|
-
assert_equal '[
|
188
|
+
assert_equal '[ pred(1,"a",0.1):- [:CUT, true]]', pred.rules.inspect
|
191
189
|
end
|
192
190
|
|
193
191
|
end
|
@@ -195,12 +193,11 @@ describe 'Porolog' do
|
|
195
193
|
describe '#cut_falicy!' do
|
196
194
|
|
197
195
|
it 'should create a falicy for its predicate and terminate solving the goal' do
|
198
|
-
|
199
|
-
arguments1 = predicate1.(1,'a',0.1)
|
196
|
+
arguments1 = pred.(1,'a',0.1)
|
200
197
|
|
201
198
|
arguments1.cut_falicy!
|
202
199
|
|
203
|
-
assert_equal '[
|
200
|
+
assert_equal '[ pred(1,"a",0.1):- [:CUT, false]]', pred.rules.inspect
|
204
201
|
end
|
205
202
|
|
206
203
|
end
|
@@ -208,14 +205,13 @@ describe 'Porolog' do
|
|
208
205
|
describe '#<<' do
|
209
206
|
|
210
207
|
it 'should create a rule for its predicate' do
|
211
|
-
|
212
|
-
arguments1 = predicate1.(1,'a',0.1)
|
208
|
+
arguments1 = pred.(1,'a',0.1)
|
213
209
|
|
214
210
|
arguments1 << [
|
215
|
-
1,2,3
|
211
|
+
pred1.(1,2,3)
|
216
212
|
]
|
217
213
|
|
218
|
-
assert_equal '[
|
214
|
+
assert_equal '[ pred(1,"a",0.1):- [p(1,2,3)]]', pred.rules.inspect
|
219
215
|
end
|
220
216
|
|
221
217
|
end
|
@@ -223,16 +219,17 @@ describe 'Porolog' do
|
|
223
219
|
describe '#evaluates' do
|
224
220
|
|
225
221
|
it 'should add a block as a rule to its predicate' do
|
226
|
-
|
227
|
-
arguments1 = predicate1.(1,'a',0.1)
|
222
|
+
arguments1 = pred.(1,'a',0.1)
|
228
223
|
|
229
224
|
line = __LINE__ ; arguments2 = arguments1.evaluates do |*args|
|
225
|
+
#:nocov:
|
230
226
|
$stderr.puts "args = #{args.inspect}"
|
227
|
+
#:nocov:
|
231
228
|
end
|
232
229
|
|
233
230
|
assert_instance_of Arguments, arguments2
|
234
231
|
|
235
|
-
part1 = "[
|
232
|
+
part1 = "[ pred(1,\"a\",0.1):- #<Proc:0x"
|
236
233
|
part2 = "@#{__FILE__}:#{line}>]"
|
237
234
|
|
238
235
|
matcher = Regexp.new(
|
@@ -242,41 +239,28 @@ describe 'Porolog' do
|
|
242
239
|
Regexp.escape(part2) +
|
243
240
|
'\Z'
|
244
241
|
)
|
245
|
-
assert_match matcher,
|
242
|
+
assert_match matcher, pred.rules.inspect
|
246
243
|
end
|
247
244
|
end
|
248
245
|
|
249
246
|
describe '#variables' do
|
250
247
|
|
251
248
|
it 'should find variable arguments' do
|
252
|
-
|
253
|
-
|
254
|
-
predicate1 = Predicate.new :alpha
|
255
|
-
arguments1 = predicate1.(1,'a',0.1,:a,2.2,:b,'b',:C,1234)
|
249
|
+
arguments1 = pred.(1,'a',0.1,:a,2.2,:b,'b',:C,1234)
|
256
250
|
variables1 = arguments1.variables
|
257
251
|
|
258
252
|
assert_equal [:a,:b,:C], variables1
|
259
253
|
end
|
260
254
|
|
261
255
|
it 'should find nested variable arguments' do
|
262
|
-
|
263
|
-
|
264
|
-
predicate1 = Predicate.new :alpha
|
265
|
-
predicate2 = Predicate.new :bravo
|
266
|
-
|
267
|
-
arguments = predicate1.(1,'a',0.1,:a,predicate2.(:P,4,:Q),2.2,:b,'b',:C,1234)
|
256
|
+
arguments = pred1.(1, 'a', 0.1, :a, pred2.(:P, 4, :Q), 2.2, :b, 'b', :C, 1234)
|
268
257
|
variables = arguments.variables
|
269
258
|
|
270
259
|
assert_equal [:a,:P,:Q,:b,:C], variables
|
271
260
|
end
|
272
261
|
|
273
262
|
it 'should find embedded variable arguments' do
|
274
|
-
|
275
|
-
|
276
|
-
predicate1 = Predicate.new :alpha
|
277
|
-
predicate2 = Predicate.new :bravo
|
278
|
-
|
279
|
-
arguments = predicate1.(1,[:e1],'a',:h/:t,0.1,:a,predicate2.(:P,[6,:r,8]/predicate2.([:z]),4,:Q),2.2,:b,'b',:C,1234)
|
263
|
+
arguments = pred1.(1, [:e1], 'a', :h/:t, 0.1, :a, pred2.(:P, [6, :r, 8]/pred2.([:z]), 4, :Q), 2.2, :b, 'b', :C, 1234)
|
280
264
|
variables = arguments.variables
|
281
265
|
|
282
266
|
assert_equal [:e1,:h,:t,:a,:P,:r,:z,:Q,:b,:C], variables
|
@@ -287,15 +271,11 @@ describe 'Porolog' do
|
|
287
271
|
describe '#goal' do
|
288
272
|
|
289
273
|
it 'should return a new goal for solving the arguments' do
|
290
|
-
|
291
|
-
|
292
|
-
predicate1 = Predicate.new :alpha
|
293
|
-
arguments1 = predicate1.(1,'a',0.1,:a,2.2,:b,'b',:C,1234)
|
274
|
+
arguments1 = pred1.(1, 'a', 0.1, :a, 2.2, :b, 'b', :C, 1234)
|
294
275
|
|
295
276
|
goal = arguments1.goal
|
296
277
|
|
297
|
-
|
298
|
-
assert_equal arguments1, goal.arguments
|
278
|
+
assert_Goal goal, :p, [1, 'a', 0.1, :a, 2.2, :b, 'b', :C, 1234]
|
299
279
|
end
|
300
280
|
|
301
281
|
end
|
@@ -303,8 +283,20 @@ describe 'Porolog' do
|
|
303
283
|
describe '#solutions' do
|
304
284
|
|
305
285
|
it 'should memoize solutions' do
|
306
|
-
|
307
|
-
|
286
|
+
args1 = Arguments.new pred, [1,2]
|
287
|
+
args2 = Arguments.new pred, [:p,:q]
|
288
|
+
|
289
|
+
args1.fact!
|
290
|
+
|
291
|
+
solve_spy = Spy.on(args2, :solve).and_call_through
|
292
|
+
|
293
|
+
assert_equal [{ p: 1, q: 2 }], args2.solutions
|
294
|
+
assert_equal [{ p: 1, q: 2 }], args2.solutions
|
295
|
+
assert_equal [{ p: 1, q: 2 }], args2.solutions
|
296
|
+
assert_equal [{ p: 1, q: 2 }], args2.solutions
|
297
|
+
assert_equal [{ p: 1, q: 2 }], args2.solutions
|
298
|
+
|
299
|
+
assert_equal 1, solve_spy.calls.size
|
308
300
|
end
|
309
301
|
|
310
302
|
end
|
@@ -312,11 +304,8 @@ describe 'Porolog' do
|
|
312
304
|
describe '#solve' do
|
313
305
|
|
314
306
|
it 'should unify and solve a simple predicate' do
|
315
|
-
|
316
|
-
|
317
|
-
alpha = Predicate.new :alpha
|
318
|
-
args1 = Arguments.new alpha, [1,2]
|
319
|
-
args2 = Arguments.new alpha, [:p,:q]
|
307
|
+
args1 = Arguments.new pred, [1,2]
|
308
|
+
args2 = Arguments.new pred, [:p,:q]
|
320
309
|
|
321
310
|
args1.fact!
|
322
311
|
|
@@ -326,14 +315,12 @@ describe 'Porolog' do
|
|
326
315
|
end
|
327
316
|
|
328
317
|
it 'should unify and solve a simple predicate with multiple solutions' do
|
329
|
-
|
330
|
-
|
331
|
-
predicate :alpha
|
318
|
+
predicate :simple
|
332
319
|
|
333
|
-
|
334
|
-
|
320
|
+
simple(1,2).fact!
|
321
|
+
simple(3,4).fact!
|
335
322
|
|
336
|
-
solutions =
|
323
|
+
solutions = simple(:p,:q).solve
|
337
324
|
|
338
325
|
assert_equal [
|
339
326
|
{ p: 1, q: 2 },
|
@@ -342,14 +329,12 @@ describe 'Porolog' do
|
|
342
329
|
end
|
343
330
|
|
344
331
|
it 'should unify and solve a simple predicate with multiple solutions involving a head and tail' do
|
345
|
-
|
332
|
+
predicate :basic
|
346
333
|
|
347
|
-
|
334
|
+
basic([1,2,3]).fact!
|
335
|
+
basic([3,4,5]).fact!
|
348
336
|
|
349
|
-
|
350
|
-
alpha([3,4,5]).fact!
|
351
|
-
|
352
|
-
solutions = alpha(:p/:q).solve
|
337
|
+
solutions = basic(:p/:q).solve
|
353
338
|
|
354
339
|
assert_equal [
|
355
340
|
{ p: 1, q: [2,3] },
|
@@ -357,9 +342,7 @@ describe 'Porolog' do
|
|
357
342
|
],solutions
|
358
343
|
end
|
359
344
|
|
360
|
-
it 'should unify and solve a
|
361
|
-
skip 'until Goal added'
|
362
|
-
|
345
|
+
it 'should unify and solve a normal predicate' do
|
363
346
|
predicate :likes
|
364
347
|
|
365
348
|
likes('mary','food').fact!
|
@@ -373,17 +356,28 @@ describe 'Porolog' do
|
|
373
356
|
|
374
357
|
solutions = likes(:who,:what).solve
|
375
358
|
|
376
|
-
assert_equal
|
359
|
+
assert_equal [
|
377
360
|
{ who: 'mary', what: 'food' },
|
378
361
|
{ who: 'mary', what: 'wine' },
|
379
362
|
{ who: 'john', what: 'wine' },
|
380
363
|
{ who: 'john', what: 'mary' },
|
381
|
-
],solutions
|
364
|
+
], solutions
|
382
365
|
end
|
383
366
|
|
384
|
-
it 'should
|
385
|
-
|
367
|
+
it 'should allow isnt to be defined' do
|
368
|
+
predicate :isnt
|
369
|
+
|
370
|
+
isnt(:A,:A) << [:CUT, false]
|
371
|
+
isnt(:A,:B) << [:CUT, true]
|
372
|
+
|
373
|
+
solutions = isnt(123,123).solve
|
374
|
+
assert_equal [], solutions
|
386
375
|
|
376
|
+
solutions = isnt(123,124).solve
|
377
|
+
assert_equal [{}], solutions
|
378
|
+
end
|
379
|
+
|
380
|
+
it 'should unify and solve a deeper predicate' do
|
387
381
|
predicate :male, :female, :parent
|
388
382
|
|
389
383
|
male('james1').fact!
|
@@ -420,6 +414,7 @@ describe 'Porolog' do
|
|
420
414
|
{ X: 'james2' },
|
421
415
|
], solutions
|
422
416
|
|
417
|
+
|
423
418
|
predicate :mother, :father, :sibling, :isnt
|
424
419
|
|
425
420
|
mother(:X,:M) << [
|
@@ -473,27 +468,38 @@ describe 'Porolog' do
|
|
473
468
|
end
|
474
469
|
|
475
470
|
it 'should unify and solve a predicate involving a head and tail' do
|
476
|
-
|
477
|
-
|
478
|
-
predicate :alpha, :beta, :gamma
|
471
|
+
predicate :join, :split, :head, :tail
|
479
472
|
|
480
|
-
|
481
|
-
|
473
|
+
head(1).fact!
|
474
|
+
head(4).fact!
|
475
|
+
tail([2,3]).fact!
|
476
|
+
tail([5,6]).fact!
|
482
477
|
|
483
|
-
|
484
|
-
|
485
|
-
|
478
|
+
split(:h/:t) << [
|
479
|
+
head(:h),
|
480
|
+
tail(:t),
|
486
481
|
]
|
487
482
|
|
488
|
-
|
489
|
-
|
483
|
+
join(:l) << [
|
484
|
+
split(:l)
|
490
485
|
]
|
491
486
|
|
492
|
-
solutions =
|
487
|
+
solutions = join(:l).solve
|
493
488
|
|
494
489
|
assert_equal [
|
495
|
-
{
|
496
|
-
{
|
490
|
+
{ l: [1,2,3] },
|
491
|
+
{ l: [1,5,6] },
|
492
|
+
{ l: [4,2,3] },
|
493
|
+
{ l: [4,5,6] },
|
494
|
+
],solutions
|
495
|
+
|
496
|
+
solutions = join(:p/:q).solve
|
497
|
+
|
498
|
+
assert_equal [
|
499
|
+
{ p: 1, q: [2,3] },
|
500
|
+
{ p: 1, q: [5,6] },
|
501
|
+
{ p: 4, q: [2,3] },
|
502
|
+
{ p: 4, q: [5,6] },
|
497
503
|
],solutions
|
498
504
|
end
|
499
505
|
|
@@ -541,7 +547,7 @@ describe 'Porolog' do
|
|
541
547
|
end
|
542
548
|
end
|
543
549
|
|
544
|
-
it 'should
|
550
|
+
it 'should implement simple recursion' do
|
545
551
|
skip 'until StandardPredicates added'
|
546
552
|
|
547
553
|
predicate :count
|
@@ -623,7 +629,7 @@ describe 'Porolog' do
|
|
623
629
|
end
|
624
630
|
|
625
631
|
it 'should solve a peeling off predicate' do
|
626
|
-
skip 'until
|
632
|
+
skip 'until StandardPredicates added'
|
627
633
|
|
628
634
|
predicate :size
|
629
635
|
|
@@ -650,6 +656,18 @@ describe 'Porolog' do
|
|
650
656
|
skip 'until StandardPredicates added and converted to list representation'
|
651
657
|
# TODO: convert to list representation
|
652
658
|
|
659
|
+
predicate :tower
|
660
|
+
|
661
|
+
tower(1, :X, :Y, :Z, [[:X,:Z]]).fact!
|
662
|
+
tower(:N, :X, :Y, :Z, :S) << [
|
663
|
+
gtr(:N,1),
|
664
|
+
is(:M,:N){|n| n - 1 },
|
665
|
+
tower(:M, :X, :Z, :Y, :S1),
|
666
|
+
tower( 1, :X, :Y, :Z, :S2),
|
667
|
+
tower(:M, :Y, :X, :Z, :S3),
|
668
|
+
append(:S1, :S2, :S12),
|
669
|
+
append(:S12, :S3, :S),
|
670
|
+
]
|
653
671
|
predicate :move
|
654
672
|
|
655
673
|
move(1,:X,:Y,:Z) << [
|
@@ -671,7 +689,6 @@ describe 'Porolog' do
|
|
671
689
|
'Move top disk from right to left',
|
672
690
|
'Move top disk from right to center',
|
673
691
|
'Move top disk from left to center',
|
674
|
-
'Move top disk from left to right',
|
675
692
|
'Move top disk from center to right',
|
676
693
|
'Move top disk from center to left',
|
677
694
|
'Move top disk from right to left',
|
@@ -681,6 +698,31 @@ describe 'Porolog' do
|
|
681
698
|
'Move top disk from center to right',
|
682
699
|
].map{|s| "#{s}\n" }.join
|
683
700
|
|
701
|
+
solutions = tower(3, 'left', 'middle', 'right', :moves).solve
|
702
|
+
|
703
|
+
expected_solutions = [
|
704
|
+
{
|
705
|
+
moves: [
|
706
|
+
['left', 'center'],
|
707
|
+
['left', 'right'],
|
708
|
+
['center', 'right'],
|
709
|
+
['left', 'center'],
|
710
|
+
['right', 'left'],
|
711
|
+
['right', 'center'],
|
712
|
+
['left', 'center'],
|
713
|
+
['center', 'right'],
|
714
|
+
['center', 'left'],
|
715
|
+
['right', 'left'],
|
716
|
+
['center', 'right'],
|
717
|
+
['left', 'center'],
|
718
|
+
['left', 'right'],
|
719
|
+
['center', 'right'],
|
720
|
+
]
|
721
|
+
}
|
722
|
+
]
|
723
|
+
|
724
|
+
assert_equal expected_solutions, solutions
|
725
|
+
|
684
726
|
assert_output expected_output do
|
685
727
|
solutions = move(4,'left','right','center').solve
|
686
728
|
|
@@ -695,20 +737,27 @@ describe 'Porolog' do
|
|
695
737
|
describe '#solve_for' do
|
696
738
|
|
697
739
|
it 'should solve a predicate for specified variables' do
|
698
|
-
|
740
|
+
predicate :alpha
|
699
741
|
|
742
|
+
alpha(1,2).fact!
|
743
|
+
|
744
|
+
solutions = alpha(:p,:q).solve_for(:q,:p)
|
745
|
+
|
746
|
+
assert_equal [[2,1]], solutions
|
747
|
+
end
|
748
|
+
|
749
|
+
it 'should solve a predicate for a specified variable' do
|
700
750
|
predicate :alpha
|
701
751
|
|
702
752
|
alpha(1,2).fact!
|
753
|
+
alpha(3,5).fact!
|
703
754
|
|
704
|
-
solutions = alpha(:p,:q).solve_for(:
|
755
|
+
solutions = alpha(:p,:q).solve_for(:q)
|
705
756
|
|
706
|
-
assert_equal [
|
757
|
+
assert_equal [2,5], solutions
|
707
758
|
end
|
708
759
|
|
709
760
|
it 'should solve a predicate with multiple solutions for specified variables' do
|
710
|
-
skip 'until Goal added'
|
711
|
-
|
712
761
|
predicate :alpha
|
713
762
|
|
714
763
|
alpha(1,2).fact!
|
@@ -718,12 +767,7 @@ describe 'Porolog' do
|
|
718
767
|
|
719
768
|
solutions = alpha(:p,:q).solve_for(:p)
|
720
769
|
|
721
|
-
assert_equal [
|
722
|
-
[1],
|
723
|
-
[3],
|
724
|
-
[5],
|
725
|
-
[5],
|
726
|
-
], solutions
|
770
|
+
assert_equal [1,3,5,5], solutions
|
727
771
|
end
|
728
772
|
|
729
773
|
end
|
@@ -731,20 +775,48 @@ describe 'Porolog' do
|
|
731
775
|
describe '#valid?' do
|
732
776
|
|
733
777
|
it 'should return true when a solution is found' do
|
734
|
-
|
778
|
+
predicate :f
|
779
|
+
|
780
|
+
f(3).fact!
|
781
|
+
|
782
|
+
assert f(3).valid?, name
|
735
783
|
end
|
736
784
|
|
737
785
|
it 'should return false when no solution is found' do
|
738
|
-
|
786
|
+
predicate :f
|
787
|
+
|
788
|
+
f(1).fact!
|
789
|
+
f(2).fact!
|
790
|
+
f(4).fact!
|
791
|
+
f(5).fact!
|
792
|
+
|
793
|
+
refute f(3).valid?, name
|
794
|
+
end
|
795
|
+
|
796
|
+
it 'should return false when a falicy is found' do
|
797
|
+
predicate :f
|
798
|
+
|
799
|
+
f(3).falicy!
|
800
|
+
|
801
|
+
refute f(3).valid?, name
|
739
802
|
end
|
740
803
|
|
741
804
|
end
|
742
805
|
|
743
806
|
describe '#dup' do
|
744
807
|
|
745
|
-
|
746
|
-
|
747
|
-
|
808
|
+
let(:args1) { pred.(1,'a',0.1,:a,2.2,:b,'b',:C,1234) }
|
809
|
+
let(:goal) { args1.goal }
|
810
|
+
let(:args2) { args1.dup(goal) }
|
811
|
+
|
812
|
+
it 'should create a new Arguments' do
|
813
|
+
assert_Arguments args1, :pred, [1,'a',0.1,:a,2.2,:b,'b',:C,1234]
|
814
|
+
|
815
|
+
refute_equal args2.__id__, args1.__id__
|
816
|
+
end
|
817
|
+
|
818
|
+
it 'should variablise the arguments for the goal' do
|
819
|
+
assert_Arguments args2, :pred, [1, 'a', 0.1, goal.variable(:a), 2.2, goal.variable(:b), 'b', goal.variable(:C), 1234]
|
748
820
|
end
|
749
821
|
|
750
822
|
end
|
@@ -761,14 +833,24 @@ describe 'Porolog' do
|
|
761
833
|
assert arguments1 == arguments2, 'Arguments with identical predicates and arguments should return true'
|
762
834
|
end
|
763
835
|
|
764
|
-
it 'should return false for Arguments with identical
|
836
|
+
it 'should return false for Arguments with non-identical arguments' do
|
765
837
|
predicate1 = Predicate.new :omega
|
766
838
|
arguments1 = predicate1.(1,'a',0.1)
|
767
839
|
|
768
840
|
predicate2 = Predicate.new :omega
|
769
841
|
arguments2 = predicate2.(1,'a',0.2)
|
770
842
|
|
771
|
-
refute arguments1 == arguments2, 'Arguments with non-identical
|
843
|
+
refute arguments1 == arguments2, 'Arguments with non-identical arguments should return false'
|
844
|
+
end
|
845
|
+
|
846
|
+
it 'should return false for Arguments with non-identical predicates' do
|
847
|
+
predicate1 = Predicate.new :omega
|
848
|
+
arguments1 = predicate1.(1,'a',0.1)
|
849
|
+
|
850
|
+
predicate2 = Predicate.new :omegb
|
851
|
+
arguments2 = predicate2.(1,'a',0.1)
|
852
|
+
|
853
|
+
refute arguments1 == arguments2, 'Arguments with non-identical predicates should return false'
|
772
854
|
end
|
773
855
|
|
774
856
|
end
|