LINUX.ORG.RU

Lisp. Написание макроса и комбинация &option &key &rest


0

0

Есть желание написать макрос, что бы не приходилось часто писать конструкции вида (let ((head (car list)) (tail (cdr list))) код). Но возникли следующие проблемы:

(defmacro xxs (lst head tail &rest body)
`(let ((,head (car ,lst))
(,tail (cdr ,lst)))
,body))

(xxs '(1 2 3) 'x 'xs
'(+ x (car xs)))

Выдаётся следующая ошибка:
LET: illegal variable specification #1=('X (CAR '(1 2 3)))
Суть ошибки вроде бы ясна, а вот способ решения не придумать.

Так же не знаю как можно расположить дерективы &option &key &rest, что бы в функции можно было бы указывать по желанию имена head и tail.

(defmacro with-head-tail (head tail list &body body)
  `(multiple-value-bind (,head ,tail)
       (values (car ,list) (cdr ,list))
     ,@body))

(with-head-tail head tail '(1 2 3)
  (format nil "~a ~a" head tail))

=> 1 (2 3)

mv ★★★★★
()

* (defmacro xxs (lst head tail &rest body)
`(let ((,head (car ,lst))
(,tail (cdr ,lst)))
,@body))

XXS


* (xxs '(1 2 3) x xs (+ x (car xs)))

3

Этого хотел?

Svoloch ★★★
()

макросы не вычисляют свои аргументы. Ну, т.е. там перед x и xs не надо кавычек.
по поводу &optional/&key  и &rest - макросы поддерживают вложенные lambda-lists.

(defmacro with-list ((list &key (head (gensym "HEAD-"))
                                (tail (gensym "TAIL-")))
                     &body body) 
  `(let ((,head (car ,list))
         (,tail (cdr ,list)))
     (declare (ignorable ,head ,tail))
     ,@body))

ну и использование:
(with-list ('(1 2 3) :head car :tail cdr) 
  (append cdr (list car)))

=> '(2 3 1)

И, кстати, список лучше занести во временную переменную. Для предотвращения двойного вычисления формы, и, соотвественно, побочных эффектов, если вдруг такие в вычисляемой форме имеются.

(defmacro with-list ((list &key (head (gensym "HEAD-"))
                                (tail (gensym "TAIL-")))
                     &body body)
  (let ((list-value (gensym "LIST-"))) 
    `(let* ((,list-value ,list)
            (,head (car ,list-value))
            (,tail (cdr ,list-value)))
       (declare (ignorable ,head ,tail))
       ,@body)))

guest-3484-2009
()

> Есть желание написать макрос, что бы не приходилось часто писать конструкции вида (let ((head (car list)) (tail (cdr list))) код).

Не нужно писать такой макрос.
(destructuring-bind (x . xs) '(1 2 3) (+ x (car xs)))

Если влом писать длинное слово "destructuring-bind", то рекомендую
вот такое:
(defmacro let1 (variable + &body progn)
"Shortcut for (let ((a b)) . c) or (destructuring-bind a b . c)"
(if (atom variable)
`(let ((,variable ,+)) ,@progn)
`(destructuring-bind ,variable ,+ ,@progn)))


Имеет две формы - либо как let с одной переменной
(let1 foo bar baz) == (let ((foo bar)) baz)
А если переменная не является атомом, то ведёт себя
ровно как destructuring-bind.

С таким макросом, можно писать красиво:
(let1 (x . xs) '(1 2 3) (+ x (car xs)))

В общем, я не ответил на вопрос, но надеюсь, что был полезен.

den73 ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.