Почему отладчик Slime не оценивает конкретное выражение в выбранном фрейме?

Я пытаюсь изучить Common Lisp с помощью книги Common Lisp: мягкое введение в символьные вычисления. Кроме того, я использую SBCL, Emacs и Slime.

К концу главы 10 автор обсуждает полезную функцию прерывания. Чтобы обеспечить фоновый контекст, он представляет эту проблемную функцию:


(defun analyze-profit (price commission-rate)
  (let* ((commission (* price commission-rate))
         (result
           (cond ((> commission 100) 'rich)
                 ((< commission 100) 'poor))))
    (format t "~&I predict you will be: ~S"
            result)
    result))

Функция работает так, как ожидалось, при вызове в REPL с такими аргументами, как:

> (analyze-profit 1600 0.15)
I predict you will be: RICH
RICH

> (analyze-profit 3100 0.02)
I predict you will be: POOR
POOR

Однако он показывает неправильный результат, когда commission равно ровно 100:

> (analyze-profit 2000 0.05)
I predict you will be: NIL
NIL

Для отладки автор вставляет в определение функцию break:


(defun analyze-profit-debugging (price commission-rate)
  (let* ((commission (* price commission-rate))
         (result
           (cond ((> commission 100) 'rich)
                 ((< commission 100) 'poor))))
    (break "Value of RESULT is ~S" result)
    (format t "~&I predict you will be: ~S"
            result)
    result))

Затем он исследует стек управления, проверяя значения локальных переменных:

введите здесь описание изображения

Я знаю, что отладчики, как известно, зависят от реализации.

В моей среде (emacs, slime, sbcl) мне удалось воспроизвести что-то подобное, следуя этому отличное руководство, написанное @Vindarel.

После помещения точки (курсора) в стек 0: и нажатия e:

Backtrace:
  0: (ANALYZE-PROFIT-DEBUGGING 2000 0.05)
  1: (SB-INT:SIMPLE-EVAL-IN-LEXENV (ANALYZE-PROFIT-DEBUGGING 2000 0.05) #<NULL-LEXENV>)
  2: (EVAL (ANALYZE-PROFIT-DEBUGGING 2000 0.05))

Я могу оценить выражение в кадре стека. Таким образом, я получаю следующие сообщения в мини-буфере:

Eval in frame (COMMON-LISP-USER)> price
=> 2000 (11 bits, #x7D0, #o3720, #b11111010000)

А также:

Eval in frame (COMMON-LISP-USER)> commission-rate
=> 0.05

К сожалению, я не могу получить доступ к основной проблеме - local variable с именем commission:

Eval in frame (COMMON-LISP-USER)> commission

Я ожидал:

100.0

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

Переменная COMMISSION не привязана. [Условие типа UNBOUND-VARIABLE]

Я дважды проверяю правописание. Кроме того, я также пытался использовать p вместо e и перемещать курсор. Однако ничего не получилось.

Как я могу проверить значение проблемной переменной commission?


person Pedro Delfino    schedule 06.06.2021    source источник


Ответы (1)


Если вы перейдете к верхнему фрейму в отладчике и нажмете Enter в этом фрейме, вы увидите, что commission не известна отладчику как локальная переменная:

Value of RESULT is NIL
   [Condition of type SIMPLE-CONDITION]

Restarts:
 0: [CONTINUE] Return from BREAK.
 1: [RETRY] Retry SLIME REPL evaluation request.
 2: [*ABORT] Return to SLIME's top level.
 3: [ABORT] abort thread (#<THREAD "new-repl-thread" RUNNING {1002D65C93}>)

Backtrace:
  0: (ANALYZE-PROFIT-DEBUGGING 2000 0.05)
      Locals:
        COMMISSION-RATE = 0.05
        PRICE = 2000
        RESULT = NIL
  1: (SB-INT:SIMPLE-EVAL-IN-LEXENV (ANALYZE-PROFIT-DEBUGGING 2000 0.05) #<NULL-LEXENV>)
  2: (EVAL (ANALYZE-PROFIT-DEBUGGING 2000 0.05))
 --more--

Проблема здесь в том, что компилятор оптимизировал некоторую отладочную информацию. Вы можете указать компилятору включить дополнительную отладочную информацию с помощью declaim в начале файла или declare в определении функции:

(defun analyze-profit-debugging (price commission-rate)
  (declare (optimize (debug 3)))
  (let* ((commission (* price commission-rate))
         (result
           (cond ((> commission 100) 'rich)
                 ((< commission 100) 'poor))))
    (break "Value of RESULT is ~S" result)
    (format t "~&I predict you will be: ~S"
            result)
    result))

Число, присвоенное debug, может быть 0, 1, 2 или 3. ; большее число означает, что больше внимания следует уделять сохранению отладочной информации.

Теперь в отладчике нажатие клавиши ввода в верхнем кадре стека показывает значение commission напрямую:

Backtrace:
  0: (ANALYZE-PROFIT-DEBUGGING 2000 0.05)
      Locals:
        COMMISSION = 100.0
        COMMISSION-RATE = 0.05
        PRICE = 2000
        RESULT = NIL
  1: (SB-INT:SIMPLE-EVAL-IN-LEXENV (ANALYZE-PROFIT-DEBUGGING 2000 0.05) #<NULL-LEXENV>)
  2: (EVAL (ANALYZE-PROFIT-DEBUGGING 2000 0.05))
 --more--

И использование команды e в верхнем кадре стека теперь работает так, как вы изначально ожидали:

Eval in frame (COMMON-LISP-USER)> commission
=> 100.0
person ad absurdum    schedule 06.06.2021