Проблема 1 Напишите процедуру PRETTY-PRINT, которая принимает один аргумент (обобщенный список) и печатает ее, используя следующее правило - PullRequest
1 голос
/ 08 мая 2019

Я новичок в Common Lisp и у меня проблема. Это звучит так: Напишите процедуру PRETTY-PRINT, которая принимает один аргумент (обобщенный список) и печатает ее, используя следующее правило:

(

...

)

Любой элемент, представляющий собой список, будет напечатан с использованием того же алгоритма, рекурсивно.

Функция должна напечатать это:

(pretty-print ' ( a (b c de) fg ))

( a 

     ( b 

       c 

       de ) 

   fg )

Я сам пару раз пытался переписать функцию и дошел до этого кода:

(defun print-list (elements)

    (cond
        ((null elements) (princ ") ")) 
        ( t

         (cond ((listp (car elements))
                    ; (princ #\Space)
                     (princ "( ")
                     (print-list (car elements))
                     (format t "~%")

               )

         )

         (if (atom (car elements))
                   (prin1 (car elements)))
                   (format t "~%")      
                   (princ #\Space)
                   (print-list (cdr elements))

        )
    ) 
)

Но не печатать то, что должен. Может ли кто-нибудь помочь мне с этим? Я боролся за неделю. Спасибо

1 Ответ

5 голосов
/ 09 мая 2019

Первый шаг: использовать обычное форматирование.См., Например, Practical Common Lisp .

(defun print-list (elements)
  (cond
    ((null elements) (princ ") ")) 
    (t
     (cond ((listp (car elements))
            ;; (princ #\Space)
            (princ "( ")
            (print-list (car elements))
            (format t "~%")))
     (if (atom (car elements))
         (prin1 (car elements)))
     (format t "~%")      
     (princ #\Space)
     (print-list (cdr elements)))))

Задача, названная по имени pretty-print, и это не выполняется ни одной стандартной функцией, поэтому давайте просто сделаем это:

(defun pretty-print (elements)
  (cond
    ((null elements) (princ ") ")) 
    (t
     (cond ((listp (car elements))
            (princ "( ")
            (pretty-print (car elements))
            (format t "~%")))
     (if (atom (car elements))
         (prin1 (car elements)))
     (format t "~%")
     (princ #\Space)
     (pretty-print (cdr elements)))))

Если мы попробуем (pretty-print '(a (b c de) fg)), мы получим:

A
 ( B
 C
 DE
 ) 

 FG
 ) 

По крайней мере, все символы находятся в правильном порядке, и, кажется, отсутствует только одна скобка.Открывающая скобка, кажется, печатается только в некоторых случаях, но отсутствует в самом начале.

Чтобы продолжить, я бы хотел немного привести себя в порядок.Вложение cond s и if s в большинстве случаев не требуется;Вы можете объединить их в один cond.Для этого отметим, что (listp (car elements)) и (atom (car elements)) кажутся взаимоисключающими.Поскольку проверка null должна выполнять ранний выход, мы сделаем это с явным возвратом, чтобы также устранить это вложение.Тогда мы получим три случая:

(defun pretty-print (elements)
  (cond ((null elements)
         (princ ") ")
         (return-from pretty-print))
        ((listp (car elements))
         (princ "( ")
         (pretty-print (car elements))
         (format t "~%"))
        ((atom (car elements))
         (prin1 (car elements))))
  (format t "~%")
  (princ #\Space)
  (pretty-print (cdr elements)))

Теперь, для этой открывающей скобки, я бы попробовал более простой тестовый случай: пустой список.

(pretty-print '())

, который печатает

)

Это не правильно.Но это именно то, что говорит первая ветка cond.Однако, если мы сделаем (princ "()") здесь, в конце каждого непустого списка будет добавлена ​​открывающая скобка.

Здесь мы объединяем две вещи: печать пустого списка и завершениепечать списка.Вы не можете различить их, если вы просто делаете простую рекурсию, потому что каждый хвост списка сам по себе является списком, а последний пустой.

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

(defun pretty-print (elements)
  (cond ((null elements)
         (princ "()")
         (return-from pretty-print))
        ((listp elements)
         (princ "(")
         (pretty-print-elements elements)
         (princ ")"))
        ((atom elements)
         (prin1 elements)))
  (format t "~%")
  (princ #\Space))

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

(defun pretty-print-elements (elements)
  (unless (null elements)
    (pretty-print (first elements))
    (pretty-print-elements (rest elements))))

Давайте попробуем это:

(pretty-print '())
()

(pretty-print '(a))
(A
 )

(pretty-print '(a (b c de) fg))
(A
 (B
 C
 DE
 )
 FG
 )

Это пока не очень красиво, но, по крайней мере, все совпадает.Осталось две проблемы: закрывающие скобки не должны находиться на новой строке, а элементы должны быть выровнены.

Сначала я перемещаю разделители между элементами в другую функцию - это фактически часть печати спискане печатать элемент.

(defun pretty-print (elements)
  (cond ((null elements)
         (princ "()")
         (return-from pretty-print))
        ((listp elements)
         (princ "(")
         (pretty-print-elements elements)
         (princ ")"))
        ((atom elements)
         (prin1 elements))))

(defun pretty-print-elements (elements)
  (unless (null elements)
    (pretty-print (first elements))
    (terpri)
    (princ #\Space)
    (pretty-print-elements (rest elements))))

Я также ввел здесь terpri, который печатает новую строку.

Теперь совершенно очевидно, что пропустить это разделение после последнего элемента:

(defun pretty-print-elements (elements)
  (unless (null elements)
    (pretty-print (first elements))
    (unless (null (rest elements))
      (terpri)
      (princ #\Space))
    (pretty-print-elements (rest elements))))

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

(pretty-print '(a (b c de) fg))
(A
 (B
 C
 DE)
 FG)

Наконец, отступ.В настоящее время все отступает на один пробел.Тем не менее, мы хотим сделать отступ больше, чем глубже мы находимся в рекурсии, поэтому нам нужно отслеживать уровень.

(defun pretty-print (elements indentation)
  (cond ((null elements)
         (princ "()")
         (return-from pretty-print))
        ((listp elements)
         (princ "(")
         (pretty-print-elements elements (1+ indentation))
         (princ ")"))
        ((atom elements)
         (prin1 elements))))

(defun pretty-print-elements (elements indentation)
  (unless (null elements)
    (pretty-print (first elements) indentation)
    (unless (null (rest elements))
      (terpri)
      (print-indentation indentation))
    (pretty-print-elements (rest elements) indentation)))

Попробуйте:

(pretty-print '(a (b c de) fg) 0)
(A
 (B
  C
  DE)
 FG)

Хорошо.Вот как вы можете отформатировать S-Expressions.Дополнительный пробел, показанный в вашем вопросе, скорее всего, неверно истолкован из некоторой распечатки.

Реализация print-indentation, а также необязательное указание начального отступа оставлены в качестве упражнений для читателя.Вы также можете сделать каждую из этих функций более краткой, взглянув на то, что на самом деле является избыточным.

...