Saturday, October 17, 2009

Notes From CLL--Format Text File

http://groups.google.com/group/comp.lang.lisp/browse_thread/thread/2b06e3d7b9a19302#

This post is about reading the file and give a format list.We can see from this post that the LOOP is very powerful. 


Q:
a txt file containing a combination of D/./*/F/1 characters. In the example below, the first row has 5 elements D, ., *, ., F and second row also has 5 elements ., ., ., ., . 


D.*.F

..1.. 



 read this data line by line, and convert it into list of lists, of individual strings.

(("D" "." *" "." "F") ("." "." "1" "." "."))
Solution:

First, there's a standard CL macro that takes care of opening and closing a file for you, so instead of:

    (let ((in (open "file1.txt" :if-does-not-exist nil)))
      (let ((main-list nil))
        (when in
          ...{body which PUSHes onto MAIN-LIST}...)
        (close in))
      main-list)

you can write this:

    (with-open-file (in "file1.txt" :if-does-not-exist nil)
      (let ((main-list nil))
        ...{body}...
        main-list))

Next, you can eliminate the LINE-STRING variable -- just use LINE, it's still in scope.

Your LOOPs just *cry* out for using the COLLECT feature of the LOOP macro instead of setting up results variables (MAIN-LIST, STRING-FORM) and PUSH'ing stuff onto them. Plus, COLLECT keeps things in forward order so you don't have to REVERSE (or NREVERSE) them. First let's do the outer LOOP:

    (with-open-file (in "file1.txt" :if-does-not-exist nil)
      (loop for line = (read-line in nil)
            while line
         collect (let ((string-form nil))
                   (loop for i from 1 to (length line) do
                     (push (subseq line (1- i) i) string-form))
                   string-form)))

and now the inner:

    (with-open-file (in "file1.txt" :if-does-not-exist nil)
      (loop for line = (read-line in nil)
            while line
         collect (loop for i from 1 to (length line)
                   collect (subseq line (1- i) i))))

Finally, you can use the FOR...ACROSS array scanning feature of LOOP to walk each LINE character by character, with STRING to turn each character back into a string:

    (with-open-file (in "file1.txt" :if-does-not-exist nil)
      (loop for line = (read-line in nil)
            while line
         collect (loop for c across line collect (string c))))

And there's the 3-line solution I mentioned before 

No comments:

Post a Comment