[racket] Trying to produce list of files, not subdirectories in a specified directory. Works from one module but not from a calling module.

From: Eli Barzilay (eli at barzilay.org)
Date: Thu May 10 22:49:10 EDT 2012

(This is a reply to a bug report, I think that a mailing-list reply is
more fitting in this case since you might have more questions.)

1. Your files depend on a particular `DG' collection -- it's usually
   best to avoid such things when you submit bug reports.

2. There is no need to do (require scheme), since the `racket'
   language is generally a superset.  You also don't need to
   explicitly use `display' for toplevel values -- just leaving the
   expressions in the file will get them printed.

3. You're using `flatten' and `display-true/false-elements' as
   something that is much better done with `filter'.  For example,
   here's a complete rewrite of your code that makes it shorter (but
   still buggy):

     #lang racket
     (provide dir-list)
     (define (dir-list-files path mode)
       (filter file-exists? (directory-list path)))
     (define (dir-list-subdirs path mode)
       (filter-not file-exists? (directory-list path)))
     (define (dir-list-both path mode)
       (directory-list path))
     (define (dir-list path mode)
       (case mode
         [(files)   (dir-list-files   path mode)]
         [(subdirs) (dir-list-subdirs path mode)]
         [(both)    (dir-list-both    path mode)]))

4. But even better -- it's now clear that the only difference is the
   filtering that you do, so an expected improvement is to fold the
   `directory-list's into a single function:

     #lang racket
     (provide dir-list)
     (define (dir-list path mode)
       (define paths (directory-list path))
       (case mode
         [(files)   (filter file-exists? paths)]
         [(subdirs) (filter-not file-exists? paths)]
         [(both)    paths]))

5. That code still has a bug which exists in your original code.  The
   thing is that `directory-list' produces paths that exist in the
   directory, but they're not complete paths.  Therefore, if you're
   currently in "/foo" and use the above function with "/bar", you get
   a list of paths from "/bar" (correctly), and then you filter them
   based on whether they exist in "/foo" (incorrectly).  One way to
   resolve this is to build and return paths that extend the input
   directory:

     #lang racket
     (provide dir-list)
     (define (dir-list path mode)
       (define paths
         (map (λ (p) (build-path path p)) (directory-list path)))
       (case mode
         [(files)   (filter file-exists? paths)]
         [(subdirs) (filter-not file-exists? paths)]
         [(both)    paths]))

   But this changes the code since it returns the extended paths
   (which might be what you want).

6. An alternative which preserves the same results is to just change
   the current directory for the filtering, or just as well for the
   whole function:

     #lang racket
     (provide dir-list)
     (define (dir-list path mode)
       (parameterize ([current-directory path])
         (define paths (directory-list))
         (case mode
           [(files)   (filter file-exists? paths)]
           [(subdirs) (filter-not file-exists? paths)]
           [(both)    paths])))


> *** Description:
> ;I have included the content of 2 files below.
> ;The first file content defines module and function called "dir-list" and a call to dir-list. Running this file succeeds in 
> returning a dir list, a list of the files in a specified directory.
> ;The second file contains same call to dir-list. Running this file fails. It returns the empty list ().  It should return a list of files.  Why not? How can I fix this? 
> 
> ;---FIRST FILE---
> #lang racket
> (require scheme)
> (require DG/all/list-to-string)
> (require DG/all/display2)
> 
> (provide dir-list)
> 
> (define (display-true-elements x y) ;(my-display x y)
>   (cond ((eq? x #t) y)
>         (else null)
>         ))
> 
> (define (display-false-elements x y) ;(my-display x y)
>   (cond ((eq? x #f) y)
>         (else null)
>         ))
> 
> (define dir-list-files
>   (lambda (path mode)
>     (let* (
>            (a (directory-list path))
>            (d (map (lambda (x) (file-exists? x)) (directory-list path)))
>            )
>       ;(display 
>        (flatten 
>        (map (lambda (number1 number2)
>               (display-true-elements number1 number2))
>             d  ;list1
>             a) ;list2
>        ;)
>       )
>       )))
> 
> (define dir-list-subdirs
>   (lambda (path mode)
>     (let* (
>            (a (directory-list path))
>            (d (map (lambda (x) (file-exists? x)) (directory-list path)))
>            )
>       ;(display 
>        (flatten 
>        (map (lambda (number1 number2)
>               (display-false-elements number1 number2))
>             d  ;list1
>             a) ;list2
>        ;)
>       )
>     )))
> 
> (define dir-list-both
>   (lambda (path mode)
>     ;(display 
>      (directory-list path)
>      ;)
>     ))
> 
> (define dir-list
>   (lambda (path mode)
>     (cond 
>       ((eq? mode 'files) (dir-list-files path mode))
>       ((eq? mode 'subdirs) (dir-list-subdirs path mode))
>       ((eq? mode 'both) (dir-list-both path mode))
>       )
>     ))
> 
> ;(display (dir-list "/home/don/.plt-scheme/4.2.1/collects/DG/test/copy/folder1" 'files)) ;=> Returns list of files.
> 
> ;---SECOND FILE---
> #lang racket
> (require scheme)
> (require DG/all/dir-list)
> 
> (display 
>  (dir-list "/home/don/.plt-scheme/4.2.1/collects/DG/test/copy/folder1" 'files)
>  ) ;=> Returns () as list of files.
> 
> *** How to repeat:
> 1) Execute first file. Returns list of files, not subdirectories.
> 2) Execute second file, which simply calls first file. Returns erroneous empty list. Should return list of files, not subdirectories.

-- 
          ((lambda (x) (x x)) (lambda (x) (x x)))          Eli Barzilay:
                    http://barzilay.org/                   Maze is Life!


Posted on the users mailing list.