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.
@@ -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
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.5.96",
6
+ "version": "1.5.97",
7
7
  "description": "EyeProlog turns facts and rules into answers and proofs.",
8
8
  "type": "module",
9
9
  "main": "./index.js",
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",
@@ -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 **229 self-contained runnable programs**. Every
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 229 normal answer goldens and the 61 selected proof goldens with:
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 229 normal examples and 61 proof examples; all executable chapter programs are parsed and their declared goals are executed. The nine-case
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.