CS 5010: Guided Practice 4.3 — Solution

Here is one solution:

    ;; inventory-total-value : Inventory -> Integer
    ;; GIVEN: a Inventory
    ;; RETURNS: the value of all the copies on hand of all the books in the
    ;; given Inventory
    ;; (inventory-total-value inv1) = 390
    
    (begin-for-test
      (check-equal? 
        (inventory-total-value empty)
        0
        "value of the empty inventory should have been 0")
      (check-equal?
        (inventory-total-value inv1)
        390
        "simple test"))
    
    ;; STRATEGY: Use template for Inventory on books
    (define (inventory-total-value books)
      (cond [(empty? books) 0]
            [else (+ (book-inventory-value (first books))
                     (inventory-total-value (rest books)))]))
  
    ;; books-out-of-stock : Inventory -> Inventory
    ;; GIVEN: a list of books
    ;; RETURNS: a list of the books that are out of stock in the given Inventory
    ;; Example:
    ;; (books-out-of-stock inv1) =
    ;;  (list
    ;;    (make-book "Shakespeare" "Hamlet" 0 2)
    ;;    (make-book "Shakespeare" "Macbeth" 0 10))
    ;; STRATEGY: Use template for Inventory on books
    
    (define (books-out-of-stock books)
      (cond [(empty? books) empty]
            [else (if (book-out-of-stock? (first books))
                      (cons (first books) (books-out-of-stock (rest books)))
                      (books-out-of-stock (rest books)))]))
    
    (check-equal?
      (books-out-of-stock inv1)
      (list
        (make-book "Shakespeare" "Hamlet" 0 2)
        (make-book "Shakespeare" "Macbeth" 0 10)))
    
    ;; book-out-of-stock? : Book -> Boolean
    ;; returns true iff the given book is out of stock
    ;; EXAMPLE: 
    ;; (book-out-of-stock? (make-book "Felleisen" "HtDP/1" 20 7)) = false
    ;; (book-out-of-stock? (make-book "Felleisen" "HtDP/1"  0 7)) = true
    ;; STRATEGY: Use template for Book on b
    (define (book-out-of-stock? b)
      (= (book-on-hand b) 0))
    
    (begin-for-test
      (check-false
        (book-out-of-stock? (make-book "Felleisen" "HtDP/1" 20 7)))
      (check-true
        (book-out-of-stock? (make-book "Felleisen" "HtDP/1"  0 7))))
    
    ;; (check-equal? ... true) would have been fine, too.  Look at the
    ;; Help Desk page for check-equal? to see some other useful checks.

Note how our program follows the data definition: the inventory contains books, so books-out-of-stock? calls book-out-of-stock?

Note that the design of the help function book-out-of-stock?, with its deliverables, was needed to make this a good solution. Writing

    (if (= (book-on-hand (first lob)) 0) ... ...)

matches the template, but is undesirable because it violates the principle of one-function-one-task, by forcing books-out-of-stock to know about both the representation of an inventory AND how being out of stock is represented for a book.

These are not the only reasonable contracts for these functions. In inventory-total-value, we could have replaced Integer with any numeric type that includes Integer, but then any function that calls inventory-total-value could not assume that the value returned by inventory-total-value was an integer. We could not replace it with NonNegInteger, since the data definition for BookStatus allows the price field to be negative (!).