Furkan Kalabalık
Furkan Kalabalık

Reputation: 13

Function returns list but prints out NIL in LISP

I'm reading a file char by char and constructing a list which is consist of list of letters of words. I did that but when it comes to testing it prints out NIL. Also outside of test function when i print out list, it prints nicely. What is the problem here? Is there any other meaning of LET keyword?

This is my read fucntion:

(defun read-and-parse (filename)
  (with-open-file (s filename)
    (let (words)
      (let (letter)
        (loop for c = (read-char s nil)
              while c
              do(when (char/= c #\Space)
                  (if (char/= c #\Newline) (push c letter)))
              do(when (or (char= c #\Space) (char= c #\Newline) )
                  (push (reverse letter) words)
                  (setf letter '())))
        (reverse words)
))))

This is test function:

(defun test_on_test_data ()

    (let (doc (read-and-parse "document2.txt"))
        (print doc)
))

This is input text:

hello
this is a test

Upvotes: 1

Views: 417

Answers (2)

user5920214
user5920214

Reputation:

Barmar's answer is the right one. For interest, here is a version of read-and-parse which makes possibly-more-idiomatic use of loop, and also abstracts out the 'is the character white' decision since this is something which is really not usefully possible in portable CL as the standard character repertoire is absurdly poor (there's no tab for instance!). I'm sure there is some library available via Quicklisp which deals with this better than the below.

I think this is fairly readable: there's an outer loop which collects words, and an inner loop which collects characters into a word, skipping over whitespace until it finds the next word. Both use loop's collect feature to collect lists forwards. On the other hand, I feel kind of bad every time I use loop (I know there are alternatives).

By default this collects the words as lists of characters: if you tell it to it will collect them as strings.

(defun char-white-p (c)
  ;; Is a character white?  The fallback for this is horrid, since
  ;; tab &c are not a standard characters.  There must be a portability
  ;; library with a function which does this.
  #+LispWorks (lw:whitespace-char-p c)
  #+CCL (ccl:whitespacep c)             ;?
  #-(or LispWorks CCL)
  (member char (load-time-value
                (mapcan (lambda (n)
                          (let ((c (name-char n)))
                            (and c (list c))))
                        '("Space" "Newline" "Page" "Tab" "Return" "Linefeed"
                          ;; and I am not sure about the following, but, well
                          "Backspace" "Rubout")))))

(defun read-and-parse (filename &key (as-strings nil))
  "Parse a file into a list of words, splitting on whitespace.

By default the words are returned as lists of characters.  If
AS-STRINGS is T then they are coerced to strings"
  (with-open-file (s filename)
    (loop for maybe-word = (loop with collecting = nil
                                 for c = (read-char s nil)
                                 ;; carry on until we hit EOF, or we
                                 ;; hit whitespace while collecting a
                                 ;; word
                                 until (or (not c) ;EOF
                                           (and collecting (char-white-p c)))
                                 ;; if we're not collecting and we see
                                 ;; a non-white character, then we're
                                 ;; now collecting
                                 when (and (not collecting) (not (char-white-p c)))
                                 do (setf collecting t)
                                 when collecting
                                 collect c)
          while (not (null maybe-word))
          collect (if as-strings
                      (coerce maybe-word 'string)
                    maybe-word))))

Upvotes: 2

Barmar
Barmar

Reputation: 780688

You're not using let properly. The syntax is:

(let ((var1 val1)
      (var2 val2)
      ...)
  body)

If the initial value of the variable is NIL, you can abbreviate (varN nil) as just varN.

You wrote:

(let (doc 
      (read-and-parse "document2.txt"))
  (print doc))

Based on the above, this is using the abbreviation, and it's equivalent to:

(let ((doc nil)
      (read-and-parse "document2.txt"))
  (print doc))

Now you can see that this binds doc to NIL, and binds the variable read-and-parse to "document2.txt". It never calls the function. The correct syntax is:

(let ((doc (read-and-parse "document2.txt")))
  (print doc))

Upvotes: 5

Related Questions