0

could someone help me with clarification to one of the possible solution to exercise 3.19. the procedure mystery is infinite loop in case list cycle is given as argument. nevertheless when we use procedure eq? to check if list contains the cycle, it works and provides true value.

(define (last-pair x)     
        (if (null? (cdr x))          
            x           
            (last-pair (cdr x))      
        ) 
)            
(define (make-cycle x)                
        (set-cdr! (last-pair x) x)                         
)                    
(define (mystery x)               
        (define (loop x y)                    
                (if (null? x)                
                    y                
                    (let ((temp (cdr x)))             
                          (set-cdr! x y)                     
                          (loop temp x)                  
                    )                
                )                   
        )                 
        (loop x '())              
)             
(define t (list 1 2 3))            
(define w (make-cycle t))                 
(eq? (mystery t) t) 

it looks like magic. I would appreciate for any help.

Oliver
  • 79
  • 1
  • 8
  • How would you draw the content of the cons cells of each list ? What does `eq?` do ? is  `t` circular after you call `(make-cycle t)` ? – coredump Jul 26 '17 at 07:17
  • as far as i understand t becomes circular after the line (make-cycle t) is executed. (eq? (mystery t) t) is test to detect cycle inside list. it is true if there is cycle and false otherwise. – Oliver Jul 26 '17 at 11:03
  • if i try to execute, the line '(eq? (mystery w) w)' throws error. i do not understand why. – Oliver Jul 26 '17 at 11:12
  • `eq?` returns true when its arguments are identical (think "pointer comparison") : https://stackoverflow.com/questions/16299246/what-is-the-difference-between-eq-eqv-equal-and-in-scheme. In particular, there is no attempt to detect cycles. Unless specified otherwise, functions which accepts lists might not terminate when there is a cycle. – coredump Jul 26 '17 at 11:50

1 Answers1

4

mystery reverses an array "in-place" by repeatedly snipping off the cdr of each entry and replacing that with the cdr of the previous x.

If this list has no loop, then it will end up reversed by the time you get back to the original '(). If there is a loop, you'll have the original array's pointer.

This is definitely a tricky to understand issue. If you make a box-and-pointer diagram it will definitely help and you'll only need to draw 3 diagrams.


Automatically Generating Diagrams of Lists

In the process of doing SICP myself, I found myself wanting a way to visualize list mutation (and to skip the numerous "draw a list diagram of..." exercises). I wrote a small function for doing so and I thought you might find it helpful if I shared it.

These diagrams are an example of this function being run on x each time loop (within the mystery function) is ran.

first second third forth last

The following code is what I used for generating these diagrams. I wrote this code as a Scheme novice, but it's very simple to use: the function (list->graphviz) accepts a parameter lst which is the list you'd like a diagram of, as well as an optional argument graph-name which gives the graph a special name.

(define* (list->graphviz lst #:optional graph-name)
  """Convert a list into a set of Graphviz instructions
       `lst' is the list you'd like a diagram of
       `graph-name` is an optional parameter indicating the name you'd like to give the graph."""

  (define number 0)
  (define result "")
  (define ordinals '())
  (define (result-append! str)
    (set! result (string-append result str)))

  (define* (nodename n #:optional cell)
    (format #f "cons~a~a" n (if cell (string-append ":" cell) "")))

  (define* (build-connector from to #:optional from-cell)
    (format #f "\t~a -> ~a;~%" (nodename from from-cell) (nodename to)))

  (define (build-shape elt)
    (define (build-label cell)
      (cond ((null? cell) "/");; "∅") ; null character
            ((pair? cell) "*");; "•") ; bullet dot character
            (else (format #f "~a" cell))))
    (set! number (+ number 1))

    (format #f "\t~a [shape=record,label=\"<car> ~a | <cdr> ~a\"];~%"
            (nodename number)
            (build-label (car elt))
            (build-label (cdr elt))))

  (define* (search xs #:optional from-id from-cell)
    (let ((existing (assq xs ordinals)))
      (cond
       ;; if we're not dealing with a pair, don't bother making a shape
       ((not (pair? xs)) (result-append! "\tnothing [shape=polygon, label=\"not a pair\"]\n"))
       ((pair? existing)
        (result-append! (build-connector from-id (cdr existing) from-cell)))
       (else
        (begin
          (result-append! (build-shape xs))
          (set! ordinals (assq-set! ordinals xs number))
          (let ((parent-id number))
            ;; make a X->Y connector
            (if (number? from-id)
                (result-append! (build-connector from-id parent-id from-cell)))
            ;; recurse
            (if (pair? (car xs)) (search (car xs) parent-id "car"))
            (if (pair? (cdr xs)) (search (cdr xs) parent-id "cdr"))))))))

  (search lst)
  (string-append "digraph " graph-name " {\n" result "}\n"))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;; Here is where `mystery' begins ;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define t '(1 2 3))
(set-cdr! (cddr t) t)

(define (mystery x)
  (define (loop x y graph-num)
    (display (list->graphviz x (format #f "graph~a" graph-num)))
    (if (null? x)
        y
        (let ((temp (cdr x)))
          (set-cdr! x y)
          (loop temp x (+ 1 graph-num)))))
  (loop x '() 0))

(mystery t)

The code above code generates Graphviz graph description statements, which must then be processed by dot (Graphviz) to be rendered to a graphical format.

For example, you can run the code above and pipe it into dot:

$ scheme generate_box_ptr.scm  | dot -o ptrs.ps -Tps

This command generates a postscript file which has the advantage of separating each list into it's own page if you've run list->graphviz more than once. dot can also output PNGs, PDFs and many other file formats as the manpage describes.

zetavolt
  • 2,989
  • 1
  • 23
  • 33
  • thank you very much for the respond. But still i do not understand why in case of loop, i wil get the original array's pointer, why at all i will get something as the procedure (mystery z) will not terminate at all. – Oliver Jul 27 '17 at 06:21
  • please, could you clarify what 3 diagrams should illustrate. I would appreciate it. – Oliver Jul 28 '17 at 03:37
  • thanks a lot. i understood after i draw everything in detail. sorry for disturbing one more time. – Oliver Jul 28 '17 at 05:11
  • @Oliver I added some more information about how to automatically draw your lists. – zetavolt Jul 28 '17 at 07:12