eyeprolog 1.5.96 → 1.5.97
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.
- package/examples/declarative-fault-localization.pl +36 -0
- package/examples/knights-tour-warnsdorff.pl +46 -0
- package/examples/output/declarative-fault-localization.pl +1 -0
- package/examples/output/knights-tour-warnsdorff.pl +1 -0
- package/examples/output/reified-conditionals.pl +1 -0
- package/examples/output/universal-vs-existential-termination.pl +1 -0
- package/examples/output/vanilla-meta-interpreter.pl +1 -0
- package/examples/reified-conditionals.pl +20 -0
- package/examples/universal-vs-existential-termination.pl +29 -0
- package/examples/vanilla-meta-interpreter.pl +30 -0
- package/package.json +1 -1
- package/playground.html +5 -0
- package/the-art-of-eyeprolog.md +3 -3
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
% Declarative fault localization: when a predicate returns a wrong answer,
|
|
2
|
+
% test it again on a strictly smaller sub-goal that a correct version would
|
|
3
|
+
% still have to get right. Each step either still shows the wrong answer
|
|
4
|
+
% (the fault is at or before this depth -- keep shrinking) or the sub-goal
|
|
5
|
+
% is fine (the fault must be in what combines that correct sub-result with
|
|
6
|
+
% the rest). No stack trace or print statement is needed: only whether each
|
|
7
|
+
% successively smaller call is itself correct.
|
|
8
|
+
%
|
|
9
|
+
% buggy_list_max/2 has its comparison branches swapped in the recursive
|
|
10
|
+
% clause (a classic off-by-one-style slip), so it sometimes reports the
|
|
11
|
+
% smaller of two candidates as the maximum.
|
|
12
|
+
buggy_list_max([X], X).
|
|
13
|
+
buggy_list_max([X|Xs], Max) :-
|
|
14
|
+
buggy_list_max(Xs, Max0),
|
|
15
|
+
( X > Max0 -> Max = Max0 ; Max = X ).
|
|
16
|
+
|
|
17
|
+
fixed_list_max([X], X).
|
|
18
|
+
fixed_list_max([X|Xs], Max) :-
|
|
19
|
+
fixed_list_max(Xs, Max0),
|
|
20
|
+
( X > Max0 -> Max = X ; Max = Max0 ).
|
|
21
|
+
|
|
22
|
+
%% goal: answer(X0, X1, X2, X3)
|
|
23
|
+
|
|
24
|
+
answer(WholeListWrongAnswer, TwoElementSublistAlreadyWrong, BaseCaseIsCorrect, FixedAnswer) :-
|
|
25
|
+
% The full query already gives the wrong maximum...
|
|
26
|
+
buggy_list_max([1, 5, 3], WholeListWrongAnswer),
|
|
27
|
+
% ...and so, it turns out, does the very first recursive step alone:
|
|
28
|
+
% this two-element call is already wrong on its own, which localizes
|
|
29
|
+
% the fault to the recursive clause itself, not to how its result
|
|
30
|
+
% later combines with the outer 1.
|
|
31
|
+
buggy_list_max([5, 3], TwoElementSublistAlreadyWrong),
|
|
32
|
+
% Shrinking one step further reaches the base case, which is correct
|
|
33
|
+
% (trivially): the fault cannot be shrunk past this depth.
|
|
34
|
+
buggy_list_max([3], BaseCaseIsCorrect),
|
|
35
|
+
% Swapping the branches back gives the right answer throughout.
|
|
36
|
+
fixed_list_max([1, 5, 3], FixedAnswer).
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
% Knight's tour via Warnsdorff's rule: at each step, move to the reachable
|
|
2
|
+
% unvisited square with the fewest onward moves of its own. Preferring the
|
|
3
|
+
% most constrained square first empties the "hard" corners early, while
|
|
4
|
+
% plenty of freedom remains to reach them -- so a full tour is usually found
|
|
5
|
+
% without ever needing to backtrack. keysort/2 on Degree-Square pairs picks
|
|
6
|
+
% the least-constrained-first candidate; the cut in tour_/4's base case
|
|
7
|
+
% commits to the tour once every square is visited.
|
|
8
|
+
:- use_module(library(lists)).
|
|
9
|
+
|
|
10
|
+
board_size(5).
|
|
11
|
+
|
|
12
|
+
knight_move(p(X0, Y0), p(X, Y)) :-
|
|
13
|
+
member(d(DX, DY), [
|
|
14
|
+
d(1, 2), d(2, 1), d(-1, 2), d(-2, 1),
|
|
15
|
+
d(1, -2), d(2, -1), d(-1, -2), d(-2, -1)
|
|
16
|
+
]),
|
|
17
|
+
X is X0 + DX, Y is Y0 + DY,
|
|
18
|
+
board_size(N),
|
|
19
|
+
X >= 1, X =< N, Y >= 1, Y =< N.
|
|
20
|
+
|
|
21
|
+
degree(Square, Visited, Degree) :-
|
|
22
|
+
findall(Next, (knight_move(Square, Next), \+ member(Next, Visited)), Nexts),
|
|
23
|
+
length(Nexts, Degree).
|
|
24
|
+
|
|
25
|
+
tour_(_, Visited, SquareCount, Visited) :- length(Visited, SquareCount), !.
|
|
26
|
+
tour_(Current, Visited, SquareCount, Tour) :-
|
|
27
|
+
findall(Degree-Next, (
|
|
28
|
+
knight_move(Current, Next),
|
|
29
|
+
\+ member(Next, Visited),
|
|
30
|
+
degree(Next, Visited, Degree)
|
|
31
|
+
), Candidates),
|
|
32
|
+
Candidates \= [],
|
|
33
|
+
keysort(Candidates, [_-Best | _]),
|
|
34
|
+
tour_(Best, [Best | Visited], SquareCount, Tour).
|
|
35
|
+
|
|
36
|
+
knights_tour(Start, Tour) :-
|
|
37
|
+
board_size(N),
|
|
38
|
+
SquareCount is N * N,
|
|
39
|
+
tour_(Start, [Start], SquareCount, ReverseTour),
|
|
40
|
+
reverse(ReverseTour, Tour).
|
|
41
|
+
|
|
42
|
+
%% goal: answer(X0, X1)
|
|
43
|
+
|
|
44
|
+
answer(SquareCount, Tour) :-
|
|
45
|
+
knights_tour(p(1, 1), Tour),
|
|
46
|
+
length(Tour, SquareCount).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
answer(1, 3, 3, 5).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
answer(25, [p(1, 1), p(2, 3), p(1, 5), p(3, 4), p(5, 5), p(4, 3), p(5, 1), p(3, 2), p(4, 4), p(2, 5), p(1, 3), p(2, 1), p(4, 2), p(5, 4), p(3, 5), p(1, 4), p(2, 2), p(4, 1), p(5, 3), p(4, 5), p(2, 4), p(1, 2), p(3, 3), p(5, 2), p(3, 1)]).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
answer([2, 4, 6, 8], [1, 3, 5, 7], even, odd).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
answer([[] - "abc", "a" - "bc", "ab" - "c", "abc" - []], 4, true).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
answer([ann, pat], [jim]).
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
% Reified conditionals: a "reified" predicate turns a condition into an
|
|
2
|
+
% ordinary term (true or false) that a caller receives as data, instead of
|
|
3
|
+
% committing to one branch itself the way (->)/2 does. if_/3 then dispatches
|
|
4
|
+
% on that term. The payoff is that reusable control predicates like
|
|
5
|
+
% tfilter/3 and tpartition/4 can be built once, on top of if_/3, and applied
|
|
6
|
+
% to any reified condition -- filtering and partitioning are not each
|
|
7
|
+
% reimplemented per predicate the way they would be with hand-written
|
|
8
|
+
% (->)/2 chains.
|
|
9
|
+
:- use_module(library(reif)).
|
|
10
|
+
|
|
11
|
+
even_t(N, true) :- 0 is N mod 2, !.
|
|
12
|
+
even_t(_, false).
|
|
13
|
+
|
|
14
|
+
%% goal: answer(X0, X1, X2, X3)
|
|
15
|
+
|
|
16
|
+
answer(Evens, Odds, First, Second) :-
|
|
17
|
+
tfilter(even_t, [1, 2, 3, 4, 5, 6, 7, 8], Evens),
|
|
18
|
+
tpartition(even_t, [1, 2, 3, 4, 5, 6, 7, 8], _, Odds),
|
|
19
|
+
if_(even_t(4), First = even, First = odd),
|
|
20
|
+
if_(even_t(7), Second = even, Second = odd).
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
% Universal vs. existential termination: a query terminates *existentially*
|
|
2
|
+
% if finding one answer (or failing outright) takes finite time; it
|
|
3
|
+
% terminates *universally* only if exhausting every answer does. The two
|
|
4
|
+
% can come apart for the very same predicate, depending only on how it is
|
|
5
|
+
% called -- append/3 is the classic case.
|
|
6
|
+
%
|
|
7
|
+
% append(X, Y, [a,b,c]) has exactly as many solutions as there are ways to
|
|
8
|
+
% split a 3-element list, so asking for all of them (findall/3) terminates:
|
|
9
|
+
% this call terminates universally.
|
|
10
|
+
%
|
|
11
|
+
% append(X, Y, Z) with Z left unbound has infinitely many solutions (Z can
|
|
12
|
+
% be a list of any length), so the first one is still found immediately
|
|
13
|
+
% (existential termination holds), but findall/3 over it would never
|
|
14
|
+
% return: this call terminates existentially but not universally. once/1
|
|
15
|
+
% below deliberately stops after that first solution so this file's own
|
|
16
|
+
% checked output stays finite -- do not replace it with findall/3.
|
|
17
|
+
|
|
18
|
+
%% goal: answer(X0, X1, X2)
|
|
19
|
+
|
|
20
|
+
answer(AllSplitsOfBoundList, SplitCount, FoundFirstSplitOfUnboundList) :-
|
|
21
|
+
findall(X - Y, append(X, Y, [a, b, c]), AllSplitsOfBoundList),
|
|
22
|
+
length(AllSplitsOfBoundList, SplitCount),
|
|
23
|
+
% X2 comes out ground ([]), but Y2 and Z2 stay unbound (merely aliased
|
|
24
|
+
% to each other) -- append/3's first solution names no particular
|
|
25
|
+
% list at all, only that Y and Z must coincide. Reporting that
|
|
26
|
+
% once/1 succeeded is itself the point: existential termination only
|
|
27
|
+
% promises a first answer exists, not that it pins everything down.
|
|
28
|
+
once((append(X2, _Y2, _Z2), X2 == [])),
|
|
29
|
+
FoundFirstSplitOfUnboundList = true.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
% A vanilla meta-interpreter: solve/1 mirrors the ordinary Prolog resolution
|
|
2
|
+
% rule at the object level, using clause/2 to fetch a matching clause and
|
|
3
|
+
% conjunction to solve its body. It shows that "how Prolog executes a goal"
|
|
4
|
+
% is itself expressible as an ordinary Prolog relation, not a hidden
|
|
5
|
+
% mechanism -- the classic starting point for building custom reasoners
|
|
6
|
+
% (tracers, proof recorders, alternative search strategies) on top of plain
|
|
7
|
+
% Prolog. clause/2 only inspects dynamic procedures, hence the declarations.
|
|
8
|
+
:- dynamic(parent/2).
|
|
9
|
+
:- dynamic(grandparent/2).
|
|
10
|
+
:- dynamic(great_grandparent/2).
|
|
11
|
+
|
|
12
|
+
solve(true) :- !.
|
|
13
|
+
solve((A, B)) :- !, solve(A), solve(B).
|
|
14
|
+
solve(Goal) :-
|
|
15
|
+
clause(Goal, Body),
|
|
16
|
+
solve(Body).
|
|
17
|
+
|
|
18
|
+
parent(tom, bob).
|
|
19
|
+
parent(bob, ann).
|
|
20
|
+
parent(bob, pat).
|
|
21
|
+
parent(pat, jim).
|
|
22
|
+
|
|
23
|
+
grandparent(X, Z) :- parent(X, Y), parent(Y, Z).
|
|
24
|
+
great_grandparent(X, Z) :- parent(X, Y), grandparent(Y, Z).
|
|
25
|
+
|
|
26
|
+
%% goal: answer(X0, X1)
|
|
27
|
+
|
|
28
|
+
answer(Grandchildren, GreatGrandchildren) :-
|
|
29
|
+
findall(X, solve(grandparent(tom, X)), Grandchildren),
|
|
30
|
+
findall(X, solve(great_grandparent(tom, X)), GreatGrandchildren).
|
package/package.json
CHANGED
package/playground.html
CHANGED
|
@@ -493,6 +493,7 @@
|
|
|
493
493
|
"data-negotiation",
|
|
494
494
|
"dcg-command-parser",
|
|
495
495
|
"dcg-expression-language",
|
|
496
|
+
"declarative-fault-localization",
|
|
496
497
|
"deep-taxonomy-10",
|
|
497
498
|
"deep-taxonomy-100",
|
|
498
499
|
"deep-taxonomy-1000",
|
|
@@ -571,6 +572,7 @@
|
|
|
571
572
|
"job-shop-scheduling",
|
|
572
573
|
"json",
|
|
573
574
|
"knapsack-optimization",
|
|
575
|
+
"knights-tour-warnsdorff",
|
|
574
576
|
"knowledge-engineering-alignment-flow",
|
|
575
577
|
"knuth-bendix-completion",
|
|
576
578
|
"language",
|
|
@@ -627,6 +629,7 @@
|
|
|
627
629
|
"rdf12-trig-triple-term",
|
|
628
630
|
"rdf12-triple-term",
|
|
629
631
|
"register-allocation",
|
|
632
|
+
"reified-conditionals",
|
|
630
633
|
"relational-cube-lookup",
|
|
631
634
|
"reusable-builtins",
|
|
632
635
|
"riemann-hypothesis",
|
|
@@ -656,7 +659,9 @@
|
|
|
656
659
|
"truth-maintenance-system",
|
|
657
660
|
"turing",
|
|
658
661
|
"type-inference",
|
|
662
|
+
"universal-vs-existential-termination",
|
|
659
663
|
"uuid",
|
|
664
|
+
"vanilla-meta-interpreter",
|
|
660
665
|
"vector-similarity",
|
|
661
666
|
"vulnerability-impact",
|
|
662
667
|
"web-names",
|
package/the-art-of-eyeprolog.md
CHANGED
|
@@ -9934,7 +9934,7 @@ Review questions:
|
|
|
9934
9934
|
</figure>
|
|
9935
9935
|
|
|
9936
9936
|
The [examples directory](https://github.com/eyereasoner/eyeprolog/tree/main/examples/) is the book's executable companion. The
|
|
9937
|
-
top-level directory contains **
|
|
9937
|
+
top-level directory contains **234 self-contained runnable programs**. Every
|
|
9938
9938
|
source program has an exact answer file under
|
|
9939
9939
|
[examples/output](https://github.com/eyereasoner/eyeprolog/tree/main/examples/output/), and **61 selected programs** have a checked
|
|
9940
9940
|
explanation under [examples/proof](https://github.com/eyereasoner/eyeprolog/tree/main/examples/proof/). The thematic lists link every top-level program and open the program
|
|
@@ -10346,7 +10346,7 @@ hand.
|
|
|
10346
10346
|
|
|
10347
10347
|
#### Running and extending the corpus
|
|
10348
10348
|
|
|
10349
|
-
Run all
|
|
10349
|
+
Run all 234 normal answer goldens and the 61 selected proof goldens with:
|
|
10350
10350
|
|
|
10351
10351
|
```sh
|
|
10352
10352
|
node test/run-examples.mjs
|
|
@@ -10418,7 +10418,7 @@ accept texts outside the strict grammar, but it may not reinterpret an accepted
|
|
|
10418
10418
|
standard case.
|
|
10419
10419
|
|
|
10420
10420
|
The file-based conformance corpus contains 905 cases, including 479 focused ISO cases derived from the success, failure, mode, and error behavior in ISO/IEC 13211-1 clauses 7 and 8, Part 2 modules, and Part 3 grammar rules.
|
|
10421
|
-
Separate exact-output suites check
|
|
10421
|
+
Separate exact-output suites check 234 normal examples and 61 proof examples; all executable chapter programs are parsed and their declared goals are executed. The nine-case
|
|
10422
10422
|
playground contract suite imports the production worker, sends real reasoning
|
|
10423
10423
|
requests through its message protocol, and crawls the served module graph for
|
|
10424
10424
|
missing assets, bad MIME types, and static Node-only imports. `conformance-report.md` inventories the file-based conformance corpus and links to the live Neumerkel evidence, which includes the current WG17 syntax result.
|