1

I'm trying to write a function find which consumes a directory tree and a file name and determines whether or not a file with that name occurs in the directory tree. What i've written work with files within the first level of the tree (read!), but does not "see" the files further in. The only way to know whether the file is not in the tree is if every directory and file is checked, but some directories do not have files and others don't have directories, but some do. What i've written do far returns false for files I know are in the tree. I don't know how to write the false condition.

(define-struct dir (name dirs files))
(define-struct file (name size content))

(define fs (make-dir 'TS
                     (list (make-dir 'Text empty
                                     (list
                                      (make-file 'part1 99 empty)
                                      (make-file 'part2 52 empty)
                                      (make-file 'part3 17 empty)))
                           (make-dir 'Libs
                                     (list
                                      (make-dir 'Code empty
                                                (list
                                                 (make-file 'hang 8 empty)
                                                 (make-file 'draw 2 empty)))
                                      (make-dir 'Docs empty
                                                (list
                                                 (make-file 'read! 19 empty)))) (list)))
                     (list (make-file 'read! 10 empty))))

(define (find fs name)
  (cond
    [(not (empty? (dir-dirs fs)))
    (dp (dir-dirs fs) name)]
    [(not (empty? (dir-files fs)))
     (fc (dir-files fs) name)]
    [else false]))

(define (dp dirs name) ;consumes lists of directories
  (cond
    [(empty? dirs) false] ;I don't know what else to put here but false
    [else (find (first dirs) name)
          (dp (rest dirs) name)]))

(define (fc files name) ;consumes lists of files
  (cond
    [(empty? files) false]
    [(symbol=? name (file-name (first files))) true]
    [else (fc (rest files) name)]))
Dave
  • 11
  • 1

1 Answers1

1

Here is the solution, (see comments within the code):

(define (find fs name)
  ; either in sub-directories list OR sub-files
  (or (fc (dir-files fs) name) (dp (dir-dirs fs) name)))

(define (dp dirs name)
  (cond
    [(empty? dirs) false] ; the base case is o.k.
    [else
     ; either in the first dir OR in the rest
     (or (find (first dirs) name)
         (dp (rest dirs) name))]))

(define (fc files name) ; this is o.k.
  (cond
    [(empty? files) false]
    [(symbol=? name (file-name (first files))) true]
    [else (fc (rest files) name)]))

Data Definition

(define-struct file [name size content])
; A File is a structure: 
;   (make-file Symbol N List)

(define-struct dir [name dirs files])
; A Dir is a structure: 
;   (make-dir Symbol Dir* File*)

; A Dir* is one of: 
; – '()
; – (cons Dir Dir*)

; A File* is one of: 
; – '()
; – (cons File File*)

Examples

; TS
; ├── Libs
; │   ├── Code
; │   │   ├── draw
; │   │   └── hang
; │   └── Docs
; │       └── read!
; ├── Text
; │   ├── part1
; │   ├── part2
; │   └── part3
; └── read!


; files
(define hang (make-file 'hang 8 empty))
(define draw (make-file 'draw 2 empty))
(define read!-in-docs (make-file 'read! 19 empty))
(define read!-in-ts (make-file 'read! 10 empty))
(define part1 (make-file 'part1 99 empty))
(define part2 (make-file 'part2 52 empty))
(define part3 (make-file 'part3 17 empty))

; directories
(define code (make-dir 'Code '() (list hang draw)))
(define docs (make-dir 'Docs '() (list read!-in-docs)))
(define text (make-dir 'Text '() (list part1 part2 part3)))
(define libs (make-dir 'Libs (list code docs) '()))
(define ts (make-dir 'TS  (list text libs) (list read!-in-ts)))

Template

; Dir -> ...
(define (dir-temp d)
  (... (dir-name d) ... (dir*-temp (dir-dirs d)) ... (file*-temp (dir-files d)) ...))

; File -> ...
(define (file-temp f)
  (... (file-name f) ... (file-size f) ... (file-content f) ...))

; Dir* -> ...
(define (dir*-temp d*)
  (cond
    [(empty? d*) ...]
    [else (... (dir-temp (first d*)) ... (dir*-temp (rest d*)) ...)]))

; File* -> ...
(define (file*-temp f*)
  (cond
    [(empty? f*) ...]
    [else (... (file-temp (first f*)) ... (file*-temp (rest f*)) ...)]))

Tests

(check-expect (dir-find ts 'hang) true)
(check-expect (dir-find ts 'part1) true)
(check-expect (dir-find ts 'part99) false)
(check-expect (dir-find text 'read!) false)
(check-expect (dir-find text 'part2) true)
(check-expect (dir-find docs 'read!) true)
(check-expect (dir-find docs 'draw) false)

Functions

; Dir Symbol -> Boolean
; does d have a file with the name filename?
(define (dir-find d filename)
  (or (dir*-find (dir-dirs d) filename) (file*-find (dir-files d) filename)))

; File Symbol -> Boolean
; does file f has the name filename?
(define (file-find f filename)
  (symbol=? (file-name f) filename))

; Dir* Symbol -> Boolean
; does d* have a file with the name filename?
(define (dir*-find d* filename)
  (cond
    [(empty? d*) false]
    [else (or (dir-find (first d*) filename) (dir*-find (rest d*) filename))]))

; File* Symbol -> Boolean
; does f* have a file with the name filename?
(define (file*-find f* filename)
  (cond
    [(empty? f*) false]
    [else (or (file-find (first f*) filename) (file*-find (rest f*) filename))]))

If we use this data definition for Dir, then we can use list abstractions:

; A Dir is a structure: 
;   (make-dir Symbol [List-of Dir] [List-of File])

; Dir Symbol -> Boolean
; does d have a file with the name filename?
(define (dir-find d filename)
  (or (dir*-find (dir-dirs d) filename) (file*-find (dir-files d) filename)))

; File Symbol -> Boolean
; does file f has the name filename?
(define (file-find f filename)
  (symbol=? (file-name f) filename))

; Dir* Symbol -> Boolean
; does d* have a file with the name filename?
(define (dir*-find d* filename)
  (ormap (λ (d) (dir-find d filename)) d*))

; File* Symbol -> Boolean
; does f* have a file with the name filename?
(define (file*-find f* filename)
  (ormap (λ (f) (file-find f filename)) f*))

We can also use the power of local to remove the filename parameter

; Dir Symbol -> Boolean
; does d have a file with the name filename?
(define (dir-find d filename)
  (local (; Dir -> Boolean
          ; does d have a file with the name filename?
          (define (dir-find-lcl d)
            (or (dir*-find (dir-dirs d)) (file*-find (dir-files d))))

          ; File -> Boolean
          ; does file f has the name filename?
          (define (file-find f)
            (symbol=? (file-name f) filename))

          ; Dir* -> Boolean
          ; does d* have a file with the name filename?
          (define (dir*-find d*)
            (ormap dir-find-lcl d*))

          ; File* -> Boolean
          ; does f* have a file with the name filename?
          (define (file*-find f*)
            (ormap file-find f*)))
    (dir-find-lcl d)))

In fact, further simplifications can be made:

; Dir Symbol -> Boolean
; does d have a file with the name filename?
(define (dir-find d filename)
  (or (ormap (λ (d) (dir-find d filename)) (dir-dirs d))
      (ormap (λ (f) (symbol=? (file-name f) filename)) (dir-files d))))
Atharva Shukla
  • 2,098
  • 8
  • 21