With family trees, we saw our first real example of a problem domain with many candidate models. The process we went through on family trees, in which we refined the models as we worked with them, is an important process in any real programming project. Let's consider another example: models of filesystems. Filesystems contain two kinds of items: files and directories, which can themselves contain both files and directories. If you wanted a very simple model of a filesystem, what information would you maintain about files and directories? For files, we would maintain just their names. For directories, we would just maintain lists of their contents. A file is a symbol A directory is one of - empty, - (cons f d), where f is a file and d is a directory, or - (cons d1 d2), where d1 and d2 are directories The template for a program that processes directories is as follows: (define (file-func a-file) ...) (define (dir-func a-dir) (cond [(empty? a-dir) ...] [(file? (first a-dir)) ... (file-func (first a-dir)) ... (dir-func (rest a-dir)) ...] [(cons? (first a-dir)) ... (dir-func (first a-dir)) ... (dir-func (rest a-dir)) ...])) What are the shortcomings of this model? In the real world. both files and directories have many more attributes. For example, directories have names and files have sizes and contents. How would we redo the data definition to account for this? First, let's enhance the definition of directories. We need to make a structure to hold the additional information about directories. In the previous model, a directory was a list containing its contents. Now, the list of contents will need to move into the structure. A directory is a structure (make-dir n c) where n is a symbol and c is a list of files and directories (define-struct dir (name contents)) A list-of-files-and-directories (l-o-f-d) is one of - empty, - (cons f lofd), where f is a file, and lofd is a l-o-f-d, or - (cons d lofd), where d is a directory, and lofd is a l-o-f-d. Consider what happens if we enhance the definition of files: does it change the shape of the above data definition? No. Therefore, let's leave files as symbols for now (you can extend them to something more complicated at home for practice if you'd like). The template: (define (file-func a-file) ...) (define (dir-func a-dir) ... (dir-name a-dir) ... (lofd-func (dir-contents a-dir)) ...) (define (lofd-func a-lofd) (cond [(empty? a-lofd) ... ] [(file? (first a-lofd)) ... (file-func (first a-lofd)) ... (lofd-func (rest a-lofd)) ...] [(dir? (first a-lofd)) ... (dir-func (first a-lofd)) ... (lofd-func (rest a-lofd)) ...])) Write a program file-in-dir? which consumes a directory and a file name and returns true if the file exists in the directory and false otherwise. ;; file-in-dir? dir sym -> boolean ;; checks whether file exists in directory (define (file-in-dir? a-dir name) (file-in-lofd? (dir-contents a-dir) name)) ;; file-in-lofd? : l-o-f-d sym -> boolean ;; checks whether file exists in a list of files and directories (define (file-in-lofd? a-lofd name) (cond [(empty? a-lofd) false] [(file? (first a-lofd)) (or (is-file?? (first a-lofd) name) (file-in-lofd? (rest a-lofd) name))] [(dir? (first a-lofd)) (or (file-in-dir? (first a-lofd) name) (file-in-lofd? (rest a-lofd) name))])) ;; is-file? : file sym -> boolean ;; indicates whether this file is the one we're looking for (define (is-file?? a-file name) (symbol=? a-file name)) Write a program depth-dir, which consumes a directory and returns a number indicating how many levels of directories are in the directory tree. ;; depth-dir : directory -> num ;; determine how many levels of directories are in a directory (define (depth-dir a-dir) (+ 1 (depth-lofd (dir-contents a-dir)))) ;; depth-lofd : l-o-f-d -> number ;; determines maximum number of levels of directories within a ;; list of files and directories (define (depth-lofd a-lofd) (cond [(empty? a-lofd) 0] [(file? (first a-lofd)) (depth-lofd (rest a-lofd))] [(dir? (first a-lofd)) (max (depth-dir (first a-lofd)) (depth-lofd (rest a-lofd)))]))