函数返回列表,但在LISP中打印出NIL

函数返回列表,但在LISP中打印出NIL,lisp,common-lisp,file-read,clisp,Lisp,Common Lisp,File Read,Clisp,我正在逐字符读取一个文件,并构建一个由单词字母组成的列表。我这样做了,但当涉及到测试它打印出零。在测试函数之外,当我打印列表时,它打印得很好。这里有什么问题?LET关键字还有其他含义吗 这是我的阅读功能: (defun read-and-parse (filename) (with-open-file (s filename) (let (words) (let (letter) (loop for c = (read-char s nil)

我正在逐字符读取一个文件,并构建一个由单词字母组成的列表。我这样做了,但当涉及到测试它打印出零。在测试函数之外,当我打印列表时,它打印得很好。这里有什么问题?LET关键字还有其他含义吗

这是我的阅读功能:

(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)
))))
这是测试功能:

(defun test_on_test_data ()

    (let (doc (read-and-parse "document2.txt"))
        (print doc)
))
这是输入文本:

hello
this is a test
你没有正确使用let。语法是:

(let ((var1 val1)
      (var2 val2)
      ...)
  body)
(let ((doc (read-and-parse "document2.txt")))
  (print doc))
如果变量的初始值为NIL,则可以将varN NIL缩写为just varN

你写道:

(let (doc 
      (read-and-parse "document2.txt"))
  (print doc))
基于上述情况,这是使用缩写,相当于:

(let ((doc nil)
      (read-and-parse "document2.txt"))
  (print doc))
现在您可以看到,这将doc绑定到NIL,并将变量read和parse绑定到document2.txt。它从不调用函数。正确的语法是:

(let ((var1 val1)
      (var2 val2)
      ...)
  body)
(let ((doc (read-and-parse "document2.txt")))
  (print doc))
你没有正确使用let。语法是:

(let ((var1 val1)
      (var2 val2)
      ...)
  body)
(let ((doc (read-and-parse "document2.txt")))
  (print doc))
如果变量的初始值为NIL,则可以将varN NIL缩写为just varN

你写道:

(let (doc 
      (read-and-parse "document2.txt"))
  (print doc))
基于上述情况,这是使用缩写,相当于:

(let ((doc nil)
      (read-and-parse "document2.txt"))
  (print doc))
现在您可以看到,这将doc绑定到NIL,并将变量read和parse绑定到document2.txt。它从不调用函数。正确的语法是:

(let ((var1 val1)
      (var2 val2)
      ...)
  body)
(let ((doc (read-and-parse "document2.txt")))
  (print doc))

巴尔马的答案是正确的。有趣的是,这里有一个版本的read and parse,它可能更习惯于使用循环,并且还抽象出了“是白色字符”的决定,因为这在portable CL中确实是不可能的,因为标准字符库非常糟糕,例如,没有制表符!。我相信有一些图书馆可以通过它来更好地处理这个问题

我认为这是相当可读的:有一个外循环收集单词,还有一个内循环收集单词中的字符,跳过空格直到找到下一个单词。两者都使用循环的收集功能来向前收集列表。另一方面,每次我使用loop时,我都感觉很糟糕,我知道还有其他选择

默认情况下,这会将单词收集为字符列表:如果您告诉它,它会将它们收集为字符串

(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))))

巴尔马的答案是正确的。有趣的是,这里有一个版本的read and parse,它可能更习惯于使用循环,并且还抽象出了“是白色字符”的决定,因为这在portable CL中确实是不可能的,因为标准字符库非常糟糕,例如,没有制表符!。我相信有一些图书馆可以通过它来更好地处理这个问题

我认为这是相当可读的:有一个外循环收集单词,还有一个内循环收集单词中的字符,跳过空格直到找到下一个单词。两者都使用循环的收集功能来向前收集列表。另一方面,每次我使用loop时,我都感觉很糟糕,我知道还有其他选择

默认情况下,这会将单词收集为字符列表:如果您告诉它,它会将它们收集为字符串

(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))))

仅供参考,您可以在一个LET中绑定多个变量:LET words letter…仅供参考,您可以在一个LET中绑定多个变量:LET words letter…顺便说一句,显示Tab是很奇怪的,但标准语法没有提到它。我认为在实践中,即使严格来说不是便携的,带t的peek-char也可以相对较好地工作。@coredump:peek-char技巧很聪明:谢谢!它并没有完全取代对“is character white”谓词的需要,但在许多情况下(包括本例),它确实避免了这种情况,尽管我没有更改代码。我只记得一半,现在我发现像tab这样的东西是半标准的:它们可能不存在,但如果它们存在,就必须被称为半标准。您可以通过名称字符选项卡检查它们是否存在。我已经增强了我的char-white-p来做这件事。顺便说一句,显示Tab很奇怪,但是标准语法没有提到它。我认为在实践中,即使严格来说不是便携的,带t的peek-char也可以相对较好地工作。@coredump:peek-char技巧很聪明:谢谢!它并没有完全取代对“is character white”谓词的需要,但在许多情况下(包括本例),它确实避免了这种情况,尽管我没有更改代码。我只记得一半,现在我发现像tab这样的东西是半标准的:它们可能不存在,但如果它们存在,就必须被称为半标准。您可以通过名称字符选项卡检查它们是否存在。我已经增强了我的char-white-p来做那件事。