函数 returns 列表但在 LISP 中打印出 NIL

Function returns list but prints out NIL in LISP

我正在一个字符一个字符地读取一个文件,并构建一个由单词字母列表组成的列表。我这样做了,但是当涉及到测试时,它打印出 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)

如果变量的初始值为NIL,可以将(varN nil)简写为varN.

您写道:

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

综合以上,这是使用缩写,相当于:

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

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

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

Barmar 的回答是正确的。有趣的是,这是 read-and-parse 的一个版本,它可能更惯用地使用 loop,并且还抽象出 'is the character white' 的决定,因为这在作为标准字符集的便携式 CL 非常差(例如没有制表符!)。我确定通过 Quicklisp 可以使用一些库,它比下面的更好地处理这个问题。

我认为这相当可读:有一个收集单词的外循环,以及一个将字符收集成单词的内循环,跳过空格直到找到下一个单词。两者都使用 loopcollect 功能来收集转发列表。另一方面,每次使用 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))))