Обратные кавычки, кавычки и сплайсинг в нормальных функциях

Я все еще нахожусь в процессе понимания макросов, и, хотя мне кажется, что я понимаю основы "обратного цитирования", "удаления цитаты" и "объединения без кавычек", я думал, что они используются / полезны только в макросах.

однако я наткнулся на этот код Common Lisp из кода Rosetta ( задача календаря),

(defun month-strings (year month)
  "Collect all of the strings that make up a calendar for a given
    MONTH and YEAR."
  `(,(date-calc:center (month-to-word month) (length *day-row*))
    ,*day-row*
     ;; We can assume that a month calendar will always fit into a 7 by 6 block
     ;; of values. This makes it easy to format the resulting strings.
      ,@ (let ((days (make-array (* 7 6) :initial-element nil)))
           (loop :for i :from (date-calc:day-of-week year month 1)
                 :for day :from 1 :to (date-calc:days-in-month year month)
                :do (setf (aref days i) day))
           (loop :for i :from 0 :to 5
             :collect
             (format nil "~{~:[  ~;~2,d~]~^ ~}"
                 (loop :for day :across (subseq days (* i 7) (+ 7 (* i 7)))
                  :append (if day (list day day) (list day))))))))

здесь обратная кавычка, расстановка кавычек и расстановка кавычек используются в обычной функции, и она работает для создания списка строк.

и хотя я не использую Scheme, решение Racket имело нечто подобное,

(define days
    (let ([? (if (= mn 12) (λ(x y) y) (λ(x y) x))])
      (round (/ (- (find-seconds 0 0 12 1 (? (+ 1 mn) 1) (? yr (+ 1 yr))) s)
                60 60 24))))
  (list* (~a mname #:width 20 #:align 'center) "Su Mo Tu We Th Fr Sa"
         (map string-join
              (nsplit 7 `(,@(make-list pfx "  ")
                          ,@(for/list ([d days])
                              (~a (+ d 1) #:width 2 #:align 'right))
                          ,@(make-list (- 42 pfx days) "  ")))))))

который я не проверял.

мои вопросы

Зачем это нужно в функции, каков вариант использования?

а чем он отличается от макроса?

3 ответа

Квазицитаты, unquote и unquote-splicing являются просто синтаксическим сахаром для комбинации цитируемых данных, list, а также cons, Вообразите это:

`(,a b c)    ; == (cons a '(b c))
`(a b ,c)    ; == (list 'a 'b c)
`(a b ,@c d) ; == (cons 'a (cons 'b (append c '(d))))  

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

Если у вас есть функция, которая создает структуру списка, квазицитат сделает код более понятным и удобным, так как форма будет больше похожа на результат. Он ничем не отличается от макроса, поскольку оба создают структуру списка. Макрос отличается тем, что происходит с результатом. В функции возвращается значение, а в макросе код заменяется.

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

(macroexpand '`(,a ,b ,@c))
; ==> (cons a (cons b c)) 
; ==> t

кавычка

Смотрите CLHS на Backquote.

Пример

Пример похож на этот код:

CL-USER 14 > (let ((a 1)
                   (b 2)
                   (c '(3 4 5)))
               `(,a                ; a gets evaluated and put in
                 ,b                ; b gets evaluated and put in
                 ,@c))             ; c gets evaluated and spliced in
(1 2 3 4 5)

Эффект приведенного выше кода похож на использование функции list*,

CL-USER 15 > (let ((a 1)
                   (b 2)
                   (c '(3 4 5)))
               (list* a b c))     
(1 2 3 4 5)

Какая версия вы используете, это в основном дело вкуса.

list* создает список первых значений и помещает их перед последним значением, которое должно быть списком.

Создание списка

Есть много способов и стилей для создания списка. Два здесь:

  1. использовать вложенные функции, такие как cons, list, list*, append... Это особенно полезно, когда нужно вычислить много элементов.
  2. использовать шаблоны списков с оператором обратной цитаты и ,, ,@, ,. для оценки. Это особенно полезно, когда есть вложенные списки с фиксированными объектами и несколькими объектами для вычисления.

Таким образом, способ с кавычками полезен, когда вы думаете о шаблонах списков для заполнения. Всякий раз, когда вы хотите создать (вложенные) списки, которые основаны на шаблонах с потерей постоянной структуры (то есть объектов и вложенности), это способ сделать это. Это не ограничивается макросами - это общий механизм для создания списков.

Вы также можете думать о шаблонах как об инверсии:

  1. функции оцениваются по умолчанию, а константные элементы должны быть заключены в кавычки
  2. Шаблоны обратных цитат не оцениваются по умолчанию, а переменные элементы необходимо заключать в кавычки.

Предупреждение

Сами выражения в кавычках не обязательно должны быть чистыми списками. Внутреннее представление выражений в кавычках не определено, и реализации на самом деле отличаются.

Векторы тоже

Обратите внимание, что это работает и для векторов:

CL-USER 20 > (let ((a 1)
                   (b 2)
                   (c '(3 4 5)))
               `#(,a
                  ,b
                  ,@c))
#(1 2 3 4 5)

Quasi-Quote (QQ) - это конструктор списка в Scheme.

Он более гибкий, чем кавычка ('), список или минусы, потому что он позволяет смешивать символы с оценками выражений.

QQ имеет два вспомогательных механизма:

  1. unquote - обозначается (,)

  2. unquote-splicing - обозначается (,@)

Когда используется QQ, инициируется контекст цитаты. От кавычек позволяет нам на мгновение избежать контекста кавычек и оценить выражение сразу после кавычек. Объединение цитат как исключает весь список из контекста цитаты, так и "разворачивает" список. Рассматривать:

(define b 5)
(define s (list 1 2))

Обратите внимание на различия в значениях следующих выражений: Quote, Quasi-Quote.

Входные данные:

'(a b c)
`(a b c)

Выход

> (a b c)    
> (a b c)

вход

'(a ,b c)    
`(a ,b c)

Выход

> (a ,b c)   
> (a 5 c)

вход

'(a ,s c)    
`(a ,s c)

Выход

> (a ,s c)   
> (a (1 2) c)

вход

'(a ,@s c)   
`(a ,@s c)

Выход

> (a ,@s c)  
> (a 1 2 c)

Источник: курс по компиляции, который я взял, Кулинарная книга Common Lisp - Макросы и обратная цитата

Другие вопросы по тегам