Функция возвращает список, но выводит NIL в LISP - PullRequest
1 голос
/ 16 октября 2019

Я читаю файл char по char и создаю список, который состоит из списка букв слов. Я сделал это, но когда дело доходит до тестирования, он печатает 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

Ответы [ 2 ]

5 голосов
/ 16 октября 2019

Вы не используете 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))
2 голосов
/ 17 октября 2019

Бармар ответ правильный. Для интереса, вот версия read-and-parse, которая, возможно, более идиоматично использует loop, а также абстрагирует решение «является символом белого», поскольку это действительно нереально в портативном CL, так какстандартный репертуар персонажей абсурдно скуден (например, нет вкладки!). Я уверен, что есть некоторая библиотека, доступная через Quicklisp , которая справляется с этим лучше, чем приведенная ниже.

Я думаю, что это вполне читабельно: есть внешний цикл, который собирает слова, и внутреннийцикл, который собирает символы в слово, пропуская пробелы, пока не найдет следующее слово. Оба используют функцию loop collect для сбора списков вперед. С другой стороны, я чувствую себя немного плохо каждый раз, когда я использую 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))))
...