-
Notifications
You must be signed in to change notification settings - Fork 0
/
prolog.txt
635 lines (511 loc) · 17.6 KB
/
prolog.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
Documention for: prolog.r
Created by: coccinelle
on: 24-Nov-2004
Last updated by: coccinelle on: 15-Aug-2005
Format: text/editable
Downloaded on: 27-May-2013
The Prolog (Like) Inference Engine Documentation
prolog.r is an inference engine which can process prolog like clauses in the form of :
man [jean]
woman [mary]
human [X] [man [X]]
human [X] [woman [X]]
This quick example illustrates how to load the engine,
assert clause in the knowledge base and how to submit a goal
and how to retrieve all the possible solution:
do %prolog.r
ex-base: assert none [
man [carl]
man [paul]
man [marco]
]
print [newline "*** Man list ***"]
goal ex-base [
man [X]
(print ["==>" X "is a man."])
]
=TOC
===Specification
---The interface
The engine provide the following interface :
:ASSERT - which allows you to assert clauses into a knowledge base
:RETRACT - which allows you to retract clauses from a knowledge base
:GOAL - which allows to process a goal
:FOR-WHICH - which allows you to execute a block of REBOL code for each goal solution
:BENCH-GOAL - which allows you to bench your predicates
+++assert
USAGE:
ASSERT base clauses
DESCRIPTION:
Create or update a KB block with parsed clauses. Return the KB block.
ASSERT is a function value.
ARGUMENTS:
base -- KB block or none for a new base (Type: block none)
clauses -- Clauses block to be parsed (Type: block)
If you want to create a new knowledge base, give none for the base parameter.
If you want to add clauses to a knowledge base, give the base into which the clauses
must be added.
NB. The clause are always appended to the knowledge base.
Perhaps, it will be usefull to have a /head refinement to insert it instead of append it.
+++retract
USAGE:
RETRACT base predicat /all
DESCRIPTION:
Retract a clause from the block
RETRACT is a function value.
ARGUMENTS:
base -- The KB block (Type: block)
predicat -- The predicate to retract (Type: block)
REFINEMENTS:
/all
Retract removes all clauses for which the head of the clause
can be matched with the predicate.
NB: This is not very well tested up to now.
+++goal
USAGE:
GOAL base goals
DESCRIPTION:
Try a goal and return the number of solution
GOAL is a function value.
ARGUMENTS:
base -- The KB to use (Type: block)
goals -- The goals to try (Type: block)
+++for-which
USAGE:
FOR-WHICH base 'word goals body
DESCRIPTION:
Try a goal and execute the body for each solutions
ARGUMENTS:
base -- The KB to use (Type: block)
word -- The word or block of word to set for each solutions (will be local) (Type: word block)
goals -- The goals to try (Type: block)
body -- The block to evaluates for each solutions (Type: block)
(SPECIAL ATTRIBUTES)
throw
+++bench-goal
USAGE:
BENCH-GOAL count base goals
DESCRIPTION:
Try a goal n times and return the time to do it
BENCH-GOAL is a function value.
ARGUMENTS:
count -- (Type: integer)
base -- The KB to use (Type: block)
goals -- The goals to try (Type: block)
---Differences between Prolog syntax and prolog.r dialect
The main difference is that the prolog.r dialect use block []
instead of parenthesis (). An other difference is that the body
of the predicates is placed within a block.
For example, the (Turbo) Prolog :
grandfather (X, Y) :-
father (X, Z),
father (Z, Y).
Is in the prolog.r dialect :
grandfather [X Y] [
father [X Z]
father [Z Y]
]
This is simply because it's more simple to parse and manipulate block than
strings in REBOL and also, because when you load your script,
REBOL check missing brackets if any.
An other difference is that you can place REBOL code within your clauses.
For example, to print out some information, you can do this :
grandfather [X Y] [
father [X Z]
father [Z Y]
(print [X "is the grandfather of" Y])
]
You can also place REBOL script within the parameters of a predicate.
For example the following clauses :
sum [X Y (X + Y)]
sum [X (Z - X) Z]
sum [(Z - Y) Y Z]
Will give back the third parameter when you give only two
or check that the first plus the second equal the third when you give the three parameters.
The last difference, is that you can organize your knowledge into many base. To access to
the clauses from one base to another, you must use path, for example :
facts: assert none [
father [marco paul]
father [arnaldo marco]
]
rules: assert none [
grandfather [X Y] [
facts/father [X Z]
facts/father [Z Y]
]
]
This can be usefull to organize your knowledge base, frequently one base for the rules (your logic)
one base for the facts (the knowleddge base) and one base for the case (the deduction done
for the applied case).
Except these differences, the Prolog syntax is respected. Variables are word that begins
with an uppercase letter or an underscore (_). Others words are considered as symbol.
The CUT is available (CUT or !) and also the FAIL. CUT and FAIL are the only programed
predicates. All others predifefined predicates are standard predicates.
---Predifined predicates
prolog.r offers a set of predefined predicates:
:NOT - not [X] fails when X succeeds
:EQUAL? - equal?[X Y] succeeds when X can be unified with Y
:NOT-EQUAL? - not-equal? [X Y] fails when X can be unified with Y
:IF - if [value] succeeds when the value is not none or false.
If you want to test REBOL script, place it within parenthesis, example if [(X > Y)].
:FREE - free [X] succeeds when X has no value, in other words, when X is none
:BOUND - bound [X] succeeds when X has a value, in other words, when X is not none.
:ADD - add [X Y Z] succeeds when X + Y equals Z. With ADD you can do addition but also substraction (add [1 Y 3] return with Y set to 2)
:MULT - mult [X Y Z] succeeds when X * Y equals Z. Like with ADD, you can do multiplication but also division (mult [2 Y 8] return with Y set to 4]
:REPEAT - repeat never fails. repeat [N] fails after N time successfull (usefull for loop)
prolog.r offer also another facility which is very simple but quiet difficult to explain.
An example is much more simple:
mother [anne cathy]
mother [cathy cindy]
display-all-mother [][
mother? [X _]
(print [X "is a mother])
]
In this example you can see that I didn't define the mother? predicates.
I can do this because prolog.r defines it automaticaly in the following manner :
mother? [X Y] [
mother [X Y] !
]
With this, the predicate succeeds (only one time due to the cut)
if the predicate whitout the question mark (?) succeeds.
This prevents you to define this kind of predicate.
===Examples
Here under, you will find a set of example that I use to test the engine
---Father, mother and so on ...
Here, the facts and the predicates
ex-base: assert none [
; The facts
; ---------
man [jean]
man [pierre]
man [jacques]
man [albert]
man [yvan]
man [marco]
woman [jeanne]
woman [anne]
father [jacques pierre]
father [jean jacques]
father [jacques anne]
mother [jeanne anne]
mother [jeanne jean]
mother [anne pierre]
; The predicates
; --------------
; A person is a man or a woman
person [X] [
man [X]
]
person [X] [
woman [X]
]
; A father is a man and is the father of at least one child
father [X] [
man [X]
father? [X _]
]
; A mother is a woman and mother of at least one child
mother [X] [
woman [X]
mother? [X _]
]
; A parent is a father or a mother
parent [X Y] [
father [X Y]
]
parent [X Y] [
mother [X Y]
]
; A parent is a person which is at least one time parent
parent [X] [
person [X]
parent? [X _]
]
]
How to print who is a man
print [newline "*** List of man ***"]
goal ex-base [
man [X]
(print ["==>" X "is a man"])
]
How to print who is a person (a man or a woman)
print [newline "*** List of persons ***"]
goal ex-base [
person [X]
(print ["==>" X "is a person"])
]
How to print who is a father
print [newline "*** List of father ***"]
goal ex-base [
father [X]
(print ["==>" x "is a father"])
]
How to print who is a parent (a father or a mother)
print [newline "*** List of parents ***"]
goal ex-base [
parent [X]
(print ["==>" X "is a parent"])
]
How to print all the couples mother daughter
print [newline "*** List of mother/daughter couple (use of for-which) ***"]
for-which ex-base [
Y Z
][
mother [Y Z]
woman [Z]
][
print [Y "is the mother of" Z]
]
The following examples cannot be done with many Prolog
(Turbo Prolog and his successor Visual Prolog for example)
How to print all the relationship with anne.
print [newline "*** List of anne's relationship ***"]
goal ex-base [
X [Y anne]
(print ["==>" Y "is the" X "of anne"])
]
How to print all the couple mother daughter
print [newline "*** List of relationship between anne and pierre ***"]
goal ex-base [
X [anne pierre]
(print ["==> anne" "is the" X "of pierre"])
]
How to print all relationship two by two
print [newline "*** List of all relationship two by two ***"]
goal ex-base [
X [Y Z]
(print ["==>" mold Y "is the" X "of" mold Z])
]
How to print all the relationship
print [newline "*** List of all relationship ***"]
goal ex-base [
X Y
(print ["==>" X mold Y])
]
---Reverse and concat of list
Here are three example of list manipulation
ex-base: assert none [
; Invert of a list
invert [[|X] [|Y]] [
invert [X [] Y]
]
invert [[] X X] [!]
invert [[X | Y] Z W] [
invert [Y [X | Z] W]
]
; Concat of two lists
concat [[] L L] [!]
concat [[X | L1] L2 [X | L3]] [
concat [L1 L2 L3]
]
; Reverse known as naive reverse of a list
nrev [[] []] [!]
nrev [[X | Y] L] [
nrev [Y Z]
concat [Z [X] L]
]
]
print [newline "*** Invert of [0 1 2 3 4 5 6 7 8 9] ***"]
goal ex-base [
invert [[0 1 2 3 4 5 6 7 8 9] [] X]
(print ["==> Invert of [0 1 2 3 4 5 6 7 8 9] is" mold X])
]
print [newline "*** Concat of [0 1 2 3 4] ans [5 6 7 8 9] ***"]
goal ex-base [
concat [[0 1 2 3 4] [5 6 7 8 9] X]
(print ["==> Concat of [0 1 2 3 4] [5 6 7 8 9] is" mold X])
]
print [newline "*** Naive reverse call from outside (for which) ***"]
for-which ex-base [
Y
][
nrev [[1 2 3] Y]
][
print ["Naive reverse of [1 2 3] is" mold Y]
]
---How to optimize the cutting of a bar in many pieces
I create this example when Guy (a guy which name is Guy) asked on the french
forum how to resolve this problem in REBOL.
The predicate "combine" receive in input the length, the element list
and return the remainder and the solution.
ex-base: assert none [
; If the first element is smaller or equal to the length, it's a solution
combine [L [X1 | _] (L - X1) [X1]][
if [(L >= X1)]
]
; If the first element is smaller or equal to the length,
; we try for the rest of the length
combine [L [X1 | X] R [X1 | Y]] [
if [(L >= X1)]
combine [(L - X1) [X1 | X] R Y]
]
; If the length is greater than 0
; we try with the rest of the element
combine [L [_ | X] R Y] [
if [(L > 0)]
combine [L X R Y]
]
]
print [newline "*** Optimization of the cutting of a bar of 25 meters in element of 10 4 and 3 ***"]
result: copy []
for-which ex-base [RB C] [
combine [25 [10 4 3] RB C]
][
append result reduce [RB C]
]
sort/skip result 2
foreach [R C] result [
print ["The remainder is" R "for the combination" mold C]
]
---Resolving a murder
Here are the deducting rules
rule: assert none [
; R1
; Someone (X) could have killed a person (Y) one day (Z) if :
; X have a weapon
; X can wish to kill Y
; X does not have an alibi for day Z
can-have-killed [X Y Z] [
fact/has-a-weapon [X]
wish-to-kill [X Y]
day [Z]
does-not-have-an-alibi-for-day [X Z]
]
; R2
; Someone (X) has not an alibi for the day (Y) if :
; either he does not have an alibi for the day
; either the alibi is given by a doubtful person
does-not-have-an-alibi-for-day [X Y] [
not [fact/has-an-alibi [X Y _]]
]
does-not-have-an-alibi-for-day [X Y][
fact/has-an-alibi [X Y Z]
fact/is-doubtful [Z]
]
; R3
; Someone (X) can wish to kill a person (Y) if
; maybe if he wishes to be avenged of Y
; maybe if he is the heir of Y
; maybe if he has seen committing a crime by Y
; maybe if he must give back money to Y
wish-to-kill [X Y][
fact/wish-to-be-avenged [X Y] !
]
wish-to-kill [X Y][
fact/is-the-heir [X Y] !
]
wish-to-kill [X Y][
fact/has-seen-committing-a-crime [Y X] !
]
wish-to-kill [X Y][
fact/must-give-back-money [X Y] !
]
; List of the day of the week
day [ monday ]
day [ tuesday ]
day [ wednesday ]
day [ thursday ]
day [ friday ]
day [ saturday ]
day [ sunday ]
]
Here the result of the investigation
fact: assert none [
; The alibis
has-an-alibi [luc tuesday bernard]
has-an-alibi [paul tuesday bernard]
has-an-alibi [louis tuesday luc]
has-an-alibi [alain thursday luc]
; The doubtful person
is-doubtful [alain]
; Who wish to be avenged
wish-to-be-avenged [paul jean]
wish-to-be-avenged [luc jean]
; Who is the heir of who
is-the-heir [bernard jean]
is-the-heir [jean louis]
; Who must give back money to who
must-give-back-money [louis jean]
must-give-back-money [luc jean]
; Who has seen committing a crime
has-seen-committing-a-crime [jean alain]
; Who has a weapon
has-a-weapon [luc]
has-a-weapon [louis]
has-a-weapon [alain]
]
Here we call the goals
print [newline "*** Who killed jean Tuesday ***"]
goal rule [
can-have-killed [X jean tuesday]
(print ["==>" X "can have done it"])
]
print [newline "*** Who could be a murder ***"]
goal rule [
fact/has-a-weapon [X]
can-have-killed? [X _ _]
(print ["==>" X "could be a murder"])
]
print [newline "*** Who could be a victim ***"]
goal rule [
can-have-killed? [_ Y _]
(print ["==>" Y "could be killed"])
]
print [newline "*** When a crime could take place?***"]
goal rule [
day [Z]
can-have-killed? [_ _ Z]
(print ["==> A crime could take place" Z])
]
=== Performance
On my PC, the performance of prolog.r is around 650 LIPS (Logical Inference Per Second).
It's quiet few but this is mesured on predicate that you will never implement
in prolog dialect. The usual bench to mesure LIPS is the NREV predicate
(naive reverse of a list) :
do %engine.r
print ""
print "****************************"
print "**** Start of benchmark ****"
ex-base: assert none probe [
concat [[] L L] [!]
concat [[X | L1] L2 [X | L3]] [
concat [L1 L2 L3]
]
nrev [[] []] [!]
nrev [[X | Y] L] [
nrev [Y Z]
concat [Z [X] L]
]
]
i: 100
j: 0
t: bench-goal i ex-base [
nrev [[1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30] L]
(print ["Boucle :" j: j + 1])
]
print ["Length" t]
print ["Performance (LIPS)" (i * (30 + 1) * (30 + 2) / 2) / to-decimal t]
print ""
print "**** End of benchmark ****"
print "**************************"
First of all, I am not a specialist of programming langage. I have no particular
competence to build things like that and I make it with my own logic just for fun.
Certainly a guy with all the necessary competences will make it much more faster and smaller.
You should also take prolog.r as an add-on to REBOL and use it only for non-deterministic
logic. If you want to reverse a list, use the reverse REBOL function.
It's why it was very important to be able to embed REBOL code within the clauses
in a simple manner, as it was also important to invoke the goals easyly.
So I hope that the obtained performances are good enough for small part of a program
where it's much more easy to specify the logic with clauses. And for the remaining
part of the programm, we should write it with standard REBOL script.
Perhaps Carl will implement a native unify function which will be usefull for
prolog.r but also for other REBOL script. In the same manner, if the find/match rafinement
functioned on the blocks, prolog.r would be much faster. But this it's another story.
===Conclusion
When I was programming in Turbo Prolog, I found very tedious to write
all the deterministic part of the program with clauses that you force to be
deterministic with a lot of CUT.
I believe prolog.r, which allows the mix of REBOL and Prolog within the same script
and with a syntax continuity, is a good base to write funny script with Logic Programming.
I hope you will have fun with it, Marco.
###