Как сделать локальную строковую переменную в Лиспе? - PullRequest
0 голосов
/ 18 февраля 2020

Я пытаюсь создать локальную строковую переменную и построить строку с переменными.

(setq string-label (make-array 0 :element-type `character
                                 :fill-pointer 0
                                 :adjustable t))
(loop while (and (char/= char #\Space)
                 (char/= char #\()
                 (char/= char #\Newline))
      do
         (vector-push-extend char string-label)
         (setq char (read-char fstream nil)))

Я получаю сообщение об ошибке:

*** - SETQ: variable STRING_LABEL has no value

Ответы [ 2 ]

4 голосов
/ 18 февраля 2020

Основными конструкциями для определения локальных переменных являются let и let*. Другие операторы, полученные из let, также связывают локальные переменные, такие как with-open-file и destructuring-bind. Конечно, lambda и производные от него формы определения функций, такие как defun, имеют параметры, и это локальные переменные. (Исторически lambda существовал до let).

В конструкции loop локальные переменные могут быть определены с помощью предложения with, например:

;; Like a hidden (let ((x 42) ...) in the code generated by loop:

(loop with x = 42 ...)

Это позволяет многим экземплярам loop избегать окружения дополнительными let и, таким образом, появляться более «аккуратно».

Локальные переменные также могут автоматически переходить через итерации loop, что во многих случаях можно избежать включения неуклюжих setq форм назначения, если лог c работает правильно:

;; x is initially 1, then 2, 3, ...

(loop with x = 1 then (+ 1 x) ...)

Глобальные переменные в Common Lisp определяются с использованием defvar и defparameter, а не простым присваиванием с помощью setf или setq.

Эффект простого выдачи setf или setq для переменной, которая ранее не была определена, подвержен неудачной "серой области" ANSI Lisp, и ведет себя по-разному в разных реализациях из-за разных интерпретаций этой серой области.

В некоторых реализациях setq для неопределенной переменной создает диагностику c. В других случаях он создает специальную переменную, аналогично defparameter, а в других случаях создает нечто, напоминающее «глобальную лексическую» переменную: символ получает привязку, но не помечается как специальная переменная, оставаясь доступным для лексическое связывание.

3 голосов
/ 18 февраля 2020

Я предполагаю, что код, который вы запускаете, отличается от кода, который вы дали, поскольку в этом коде нет ссылки на string_label.

Однако с кодом, который вы используете, достаточно других проблем. мы показали, что это стоит исправить. По сути, это

  • . Вы не должны связывать переменные, не говоря уже о локальных переменных, присваивая их, и на самом деле это недопустимо в CL - вместо этого вы должны использовать форму привязки, такую ​​как let (в приведенном ниже коде я использую loop, чтобы сделать привязку для меня);
  • вы используете char до того, как он будет связан (и даже тогда он на самом деле не связан, так как вы просто используете setq);
  • условие завершения чтения метки неверно - если вы нажмете EOF, вы получите ошибку, поскольку char будет тогда nil.

Кроме того, в вашем коде есть некоторая неясность: когда вы нажимаете один из символов «стопора», хотите ли вы оставить его как следующий символ для чтения? Моя версия предполагает, что вы делаете. Конечно, это имеет значение для последовательных обращений к функции: вам нужно пропускать пробелы вперед.

Итак, вот очищенная версия вашего кода, представленная как функция. Это также делает список символов «стопора» аргументом функции и использует member, чтобы узнать, когда нужно остановиться (member использует eql по умолчанию, а eql совпадает с char= для символов так что все нормально).

(defun read-string-label (fstream &key (stoppers '(#\Space #\( #\Newline)))
  (loop with string-label = (make-array 0 :element-type 'character
                                        :fill-pointer 0
                                        :adjustable t)
        for char =  (read-char fstream nil)
        while (and char (not (member char stoppers)))
        do (vector-push-extend char string-label)
        finally (progn
                  (when char
                    ;; push the stopper character back into the stream
                    ;; so it's the next thing we read
                    (unread-char char fstream))
                  (return string-label))))
...