CS1101 Lecture 24 : Counting in cycles Let's make a simple data definition for web pages. A web page will consist of a URL and links to other web pages (we're leaving out the actual text on the pages for now). What would this look like? ;; A webpage is (make-webpage string list-of-webpage) (define-struct webpage (url links)) (define Mhome (make-webpage "www.cs.wpi.edu/~michaelg/" empty)) (define 1101home (make-webpage "web.cs.wpi.edu/~cs1101" empty)) (define CSdept (make-webpage "www.cs.wpi.edu" empty)) ;; add-link : webpage webpage -> void ;; adds link to second page from first page ;; EFFECT: change the links of the first page (define (add-link frompage topage) (set-webpage-links! frompage (cons topage (webpage-links frompage)))) (add-link Mhome 1101home) (add-link 1101home Mhome) (add-link CSdept Mhome) Now, we want to know how many pages are accessible from our page. Following the techniques we've used until now, we would write this as: ;; count-pages : webpage -> number ;; consumes web page and produces number of pages accessible from it through links (define (count-pages apage) (+ 1 (count-links (page-links apage)))) ;; count-links : list-of-webpage -> number ;; consumes list of webpages and produce total number of pages accessible from ;; pages in the list (define (count-links alol) (cond [(empty? alol) 0] [(cons? alol) (+ (count-pages (first alol)) (count-links (rest alol)))])) If we try to run this though, the program never stops. Why? Because the data has a cycle -- counting Mhome forces us to count 1101home, which forces us to count Mhome, ... How do we avoid this problem? We need to remember which pages we've already counted and make sure we don't count them again. ;; visited : list-of-url ;; stores urls that have already been counted (define visited empty) ;; count-pages : webpage -> number ;; consumes web page and produces number of pages accessible from it through links (define (count-pages apage) (cond [(in-list? (webpage-url apage) visited) 0] [else (begin (set! visited (cons (webpage-url apage) visited)) (+ 1 (count-links (webpage-links apage))))])) ;; count-links : list-of-webpage -> number ;; consumes list of webpages and produce total number of pages accessible from ;; pages in the list (define (count-links alol) (cond [(empty? alol) 0] [(cons? alol) (+ (count-pages (first alol)) (count-links (rest alol)))])) Now, if we run count-pages twice on the same page, we get 0 the second time. To make this program useful, we need to remember to reset the visited list before each time we use this to count from the interactions window. Best way to do this is with a helper function that we'll use to start the computation: ;; count-accessible : webpage -> number ;; consumes page and produces number of pages accessible from it through links ;; Effect : resets visited to empty (define (count-accessible apage) (begin (set! visited empty) (count-pages apage))) What have we seen with the family tree and URL examples? - To create data with references back and forth between two items, we need to partially create the items, then hook them up with set-structure! - Programs that can hit the same item twice need to remember which ones they've visited to make sure the program stops. This was a problem in the webpage program, but not in the family trees program (because the programs there never hit the same person more than once since they follow either child or parent connections).