A  Representing (partial) knowledge

The first programming task is to design a data definition to represent the truth values of the 729 claims. Values of this data definition should contain the truth values (true, false, or unknown) for each of the 729 claims.

Since there are 729 of them, you might naturally imagine putting the claims into a list. But, this has a tremendous performance penalty, since looking at the value of each element of the list once takes roughly (7292/2) (265720) steps. If, instead, we used a struct that had 729 fields, looking at each of these fields once would only take roughly 729 steps, a significant improvement. Of course, a structure with 729 fields is far too complicated to be useful.

Instead, we use can use a vector. A vector is similar to a struct in that it has fields and the access to each field only requires a single step. Unlike structs, however, we access the fields of a vector by numeric indices (starting at 0), rather than by field names.

To get started, design a pair of functions that convert claims to numbers between 0 and 728, and numbers between 0 and 728 to claims:

;; claim->num : claim --> number
;; to convert a claim to a number between 0 and 728.
(define (claim->num claim) ...)

;; num->claim : number --> claim
;; to convert a number between 0 and 728 to a claim
(define (num->claim n) ...)

To write these functions, it helps to think of the claim as a three-digit number, written in base nine. Here is a data definition for arbitrary numbers in base nine:

;; a base-nine-nat is either:
;;   - a digit (i.e., an integer between 0 and 8, inclusive), or
;;   - (+ (* base-nine-nat 9) digit)

where the selectors and digit predicate are:

;; digit? : base-nine-nat -> boolean
(define (digit? n) (<= n 8))

;; all-but-last-digit : base-nine-nat -> base-nine-nat
(define (all-but-last-digit b9n) (quotient b9n 9))

;; last-digit : base-nine-nat -> digit
(define (last-digit b9n) (modulo b9n 9))

;; build-vector : nat (nat --> X) --> (vector X)
;; creates a vector of size n filling all of the
;; positions in the vector with the results of f
(define (build-vector n f) ...)

;; vector-ref : (vectorof X) nat --> X
;; extracts the element at position n from the vector v
(define (vector-ref v n)

;; vector-length : (vectorof X) --> nat
;; computes the number of elements in v
(define (vector-length v) ...)

;; vector->list : (vectorof X) --> (listof X)
;; builds a list containing the elements of v
(define (vector->list v) ...)

Figure 1:  Functions on vectors from the sudoku-tp teachpack

Now that we can convert a claim into a number between zero and 728, we can represent our current knowledge of the set of true, false, and unknown claims as a vector of 729 elements. The vector that has one element for each claim, and the element at a specific position is either a boolean indicating the claim's value is known to be the boolean, or 'unknown to indicate it is not known:

;; a claim-table is:
;;  (vectorof status)

;; a status is either:
;;   - true
;;   - false
;;   - 'unknown

Using the functions in figure 1, define a library of helper functions on claims:

;; claim-true? : claim-table claim --> boolean
;; indicates if the claim is true
(define (claim-true? table claim) ...)

;; claim-false? : claim-table claim --> boolean
;; indicates if the claim is false
(define (claim-false? table claim) ...)

;; claim-unknown? : claim-table claim --> boolean
;; indicates if the claim's truth value is unknown
(define (claim-unknown? table claim) ...)

;; mostly-unknown-table : (listof claim) --> claim-table
;; constructs a claim-table where all claim are 'unknown,
;; except those in the argument claims, which are true.
(define (mostly-unknown-table claims) ...)

In addition to those, we need a way to build up tables that have more information about the truth or falsehood of particular claims. An assertion tells us the truth or falsehood of a particular claim:

;; an assertion is
;;   (make-assertion claim boolean)
(define-struct assertion (claim true?))

and the apply-assertion function records the assertion in claim table:

;; apply-assertion : assertion claim-table --> claim-table
;; builds a new table with the assertion a recorded in the table
(define (apply-assertion assertion table) ...)

;; a few examples as tests to get started
(claim-true?
  (mostly-unknown-table empty)
  (make-claim 1 1 1))
false

(claim-true?
  (apply-assertion (make-assertion (make-claim 1 1 1) true)
                   (mostly-unknown-table empty))
  (make-claim 1 1 1))
true