LINUX.ORG.RU

[Lisp] туплю с defmacro

 


0

2

Есть код:


(defun get-action-name (list)
	(mapcar #'(lambda (def-list) (list (first def-list) (second def-list))) list))

(defmacro with-new-actions (parent actions-list
							&body body)
  (get-action-name actions-list))

(defparameter *workspace-actions-list* 
  '((aNew "&New" #'new-page-handler "tab- new" "Ctrl+N")
	(aOpen "&Open" #'open-page-handler "open" "Ctrl+O")))

(with-new-actions 'parent *workspace-actions-list*) ;; 1

(with-new-actions 'parent ((aNew "&New" #'new-page-handler "tab- new" "Ctrl+N")
	(aOpen "&Open" #'open-page-handler "open" "Ctrl+O"))) ;; 2

Проблема в том что строка (2) раскрывается, а строка (1) - нет.

Вопрос как раскрыть *workspace-actions-list* внутри defmacro ? Google молчит, вот туплю и мучаюсь.

★★★

Налицо непонимание фундаментальных вещей.

Макросы:
1) раскрываются при компиляции и
2) не вычисляют свои аргументы.

anonymous
()
Ответ на: комментарий от anonymous

И, кстати, зачем в примере макросы вообще?

То, что можно сделать функцией, макросами делать не стоит.

anonymous
()
Ответ на: комментарий от anonymous

> 1) раскрываются при компиляции и 2) не вычисляют свои аргументы.

При этом при раскрытии вполне себе происходит выполнение get-action-name, при раскрытии (2) получаем как положенно:

((ANEW «&New») (AOPEN «&Open»))

Вопрос был в том - есть ли принципиальная возможность выполнить/раскрыть *workspace-actions-list* ?

Darkman ★★★
() автор топика

Попробуй так:

(defmacro with-new-actions (parent actions-list
                     &body body)
  `(get-action-name ,actions-list))
А можно ещё так, потому что body ты всё равно не используешь:
(defmacro with-new-actions (parent actions-list)
  `(get-action-name ,actions-list))

den73 ★★★★★
()
Ответ на: комментарий от anonymous

> И, кстати, зачем в примере макросы вообще?

Это маленький кусочек кода - там дальше должна идти генерация кучи let и flet.

Darkman ★★★
() автор топика
Ответ на: комментарий от Darkman

Ничего не понял, ну выполняется, и что?

Ты не понимаешь, чтоли, что, во-первых, переменные, если не определены в соответствующем eval-when, или вобще другом файле, существуют только во время выполнения программы(в run-time. а макросы, между тем, раскрываются в compile-time)? И что без вычисления(а макросы свои аргументы, в отличие от функций, не вычисляют) символ *workspace-actions-list* это просто тупо символ(просто некое имя, строка, по сути)?

при раскрытии (2) получаем как положенно:

Очевидно, потому, что макросу передаются соответствующие формы. В отличие от (1). Что тут может быть непонятного?

anonymous
()

Я так понял, ты хочешь нагенерировать биндинги для каждого пункта меню? Это может выглядеть примерно вот так:

(defmacro with-new-actions (actions &body body)
  (let ((eactions (eval actions)))
  `(let 
     ,@(iter (for (symbol name) in eactions)
             (collect `(,symbol ,name))
             )
     ,@body)))
При этом, ты можешь передать в качестве аргумента макроса либо константу (как в случае 2), либо глобальную переменную, определённую в момент компиляции (как в случае 1). Локальную переменную передать (в компилируемом коде) не получится.

den73 ★★★★★
()
Ответ на: комментарий от den73

> А можно ещё так, потому что body ты всё равно не используешь:

body потом появится. А что делать если результат надо в let загнать ? я уже в этих backquote запутался.

(defmacro with-new-actions (parent actions-list
							&body body)
  (let ((AAA (get-action-name actions-list)))
	`,AAA))

Как сдесь заставить *workspace-actions-list* раскрыться ?

Darkman ★★★
() автор топика
Ответ на: комментарий от anonymous

Я теперь понимаю, почему лисп никто не любит. Потому что лисперы, вместо того, чтобы помочь новичку, чморят его.

den73 ★★★★★
()
Ответ на: комментарий от den73

> Я так понял, ты хочешь нагенерировать биндинги для каждого пункта меню? Это может выглядеть примерно вот так:

EVAL !!! СПАСИБО ТЕБЕ ДОБРЫЙ ЧЕЛОВЕК !

Про него я и думать забыл. :-/

Darkman ★★★
() автор топика
Ответ на: комментарий от anonymous

> Ты не понимаешь, чтоли, что, во-первых, переменные, если не определены в соответствующем eval-when, или вобще другом файле, существуют только во время выполнения программы(в run-time. а макросы, между тем, раскрываются в compile-time)? И что без вычисления(а макросы свои аргументы, в отличие от функций, не вычисляют) символ *workspace-actions-list* это просто тупо символ(просто некое имя, строка, по сути)?

Ё моё ! Да знаю я про eval-when и про всякие -time - не надо мне лекции читать. Был задан конкретный вопрос, на который только den73 и ответил - всего-навсего упомянув одно лишь слово - EVAL.

Darkman ★★★
() автор топика
Ответ на: комментарий от den73

Вообще-то, это просто самые основы. Они в любой книжке/тьюториалу объясняются на первых, буквально, страницах. И естественно, с ними надо разобраться, прежде чем пробовать что-то писать.

Потому что вопрос выглядит как

void vydelit_pamyat(int* ukaz)
{
    ukaz = malloc(100);
}

void main()
{
    int* ukazatel;
    vydelit_pamyat(ukazatel);
    ukazatel[0] = 123; //Pochemu oshibka????
}

anonymous
()
Ответ на: комментарий от Darkman

Нет, EVAL это не ответ.
Eval использовать вообще не желательно, а тут так и совсем вредно.

anonymous
()
Ответ на: комментарий от Darkman

Твой «конкретный вопрос» в контексте сишного примера выше выглядел бы как «почему программа не выделяет память?».

Про eval-when и т.п. ты, очевидно, все же не знаешь. Вернее, не знаешь про то, что такое время выполнения, что такое время компиляции, ну и т.п. И почему значение переменной при компиляции еще не существует.

anonymous
()
Ответ на: комментарий от Darkman

Лучше всего будет, если ты напишешь, что ты хочешь получить на выходе. Потому что пока что я не совсем понял, что ты хочешь.

den73 ★★★★★
()
Ответ на: комментарий от den73

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

А вот твой пример с eval - вот в следующий раз он сконпилирует его из чистой лисп-системы и охренеет от того, что нифига не работает(переменная то еще мало того, что не забиндена, так вообще еще не существует при компиляции).

anonymous
()
Ответ на: комментарий от Darkman

Квазицитирование работает примерно так же, как конкатенация строк. `(a ,b c) в лиспе - примерно то же самое, что "(a,«+b+»,c)" в «обычном» языке, где генерация кода производится сложением строк.

den73 ★★★★★
()
Ответ на: комментарий от anonymous

Они и не нужны тут, насколько я понял

Смотря, что он хочет. Может, он хочет сделать локальный биндинг для каждого пункта меню, чтобы получилось:

(flet 
  ((open-main-menu ...)
   (open-edit-menu ...)
   )
  тра-ля-ля)

В этом случае без eval, по-моему, не обойтись, т.к. нужно сделать явное вычисление во время компиляции. Насчёт чистой лисп системы я его предупредил, так что меня привлечь к ответственности за это не получится.

den73 ★★★★★
()
Ответ на: комментарий от den73

> Я теперь понимаю, почему лисп никто не любит. Потому что лисперы,

вместо того, чтобы помочь новичку, чморят его.


Дело не в лисперах, а в ЛОР-е - здесь по другому вообще общаться не могут, особенно про лисп. Поэтому, вопросы технического характера лучше задавать на http://lisper.ru/forum/common-lisp - во всяком случае там нет агрессивно настроенных анонимусов.

archimag ★★★
()
Ответ на: комментарий от den73

>В этом случае без eval, по-моему, не обойтись, т.к. нужно сделать явное вычисление во время компиляции.

В этом и проблема, что оно не нужно, а в данном конкретном случае так вообще совершенно ошибочно.

anonymous
()
Ответ на: комментарий от archimag

Автор, не верь ему.

Там максимум что обсуждается, так это «как из CL сделать PHP»:
http://lisper.ru/forum/restas

и разнообразная муть типа подобной:
http://lisper.ru/forum/thread/451

Вменяемые ответы можно получить от силы на lisp@conference.jabber.ru, вот тут(ЛОР), ну и на англоязычных ресурсах(comp.lang.lisp, #lisp на freenode.net etc.)

anonymous
()
Ответ на: комментарий от den73

> Потому что действительно может оказаться, что ни макросы, ни eval здесь не нужны.

Нужно генерировать кучу однообразного кода внутри defun. Вынести на глобальный уровень нельзя, для каждого окна надо генерировать новые actions по заданному шаблону. Код параметризируется парой параметров.

В результате вот этого:

(defun get-action-name (list parent)
	(mapcar #'(lambda (def-list)
				(list (first def-list) (list 'new-action (second def-list) parent))) list))

(defun get-action-handlers (list)
  (mapcar #'(lambda (def-list)
			  (append '(action-with-data)
					  (list (first def-list))
					(cddr def-list)))
		  list))

(defmacro with-new-actions (parent get-data actions-list
							&body body)
  (let ((action-name (get-action-name (eval actions-list) parent))
		(action-handler (get-action-handlers (eval actions-list))))
	`(let ,action-name
	   (flet ((action-with-data (action handler icon shortcut))
			  (set-action action handler icon :shortcut shortcut)
			  (qfun action "setData" (qnew "QVariant(int)" ,get-data)))
		 ,@action-handler)
	   ,@body)))

Код в куче мест сократился раза в три, плюс шаблоны actions задаются в одном месте, а не размазаны по коду.

Darkman ★★★
() автор топика
Ответ на: комментарий от anonymous

> (переменная то еще мало того, что не забиндена, так вообще еще не существует при компиляции).

Вот это как раз и решается eval-when. Про эти грабли я давно знаю.

Darkman ★★★
() автор топика
Ответ на: комментарий от Darkman

Если она, переменная, при компиляции определяется, то тут два случая:

1) Она константа. В таком случае можно и ручками написать(захардкодить), нефиг переменную городить, ну или использовать ее только внутри макроса, не передавая ниоткуда снаружи(в явном виде, по кр. мере).
2) Она не константа. Но тут опять же - если она, переменная определяется другими макросами и нужна только при компиляции, то, фактически, см. 1) Если же она определяется в рантайме - то не нужно ее вычислять, нужно откладывать вычисление до времени выполнения. Т.е. eval'ить ее при компиляции не имеет смысла в любом случае.

anonymous
()
Ответ на: комментарий от anonymous

> В этом и проблема, что оно не нужно, а в данном конкретном случае так вообще совершенно ошибочно.

В чём ошибочно ?

Darkman ★★★
() автор топика
Ответ на: комментарий от anonymous

> 2) Она не константа. Но тут опять же - если она, переменная определяется другими макросами и нужна только при компиляции, то, фактически, см

Код нужен внутри десятка defun. Хардкодить шаблоны actions прямо в defun - уродство. А от пары eval при компиляции никто не умрёт.

Darkman ★★★
() автор топика
Ответ на: комментарий от Darkman

А если в actions-list какой-то код, в т.ч. вредоносный?
Ну и вообще, выглядит как-то излише громоздко.

Лучше скажи что тебе надо, я тебе напишу код, и объясню почему именно так.

anonymous
()
Ответ на: комментарий от Darkman

Шаблоны actions во всех этих defun одни и те же?

А от пары eval при компиляции никто не умрёт.

EVAL в CL в 99.999% случаев - непонимание как задачи, так и CL.

anonymous
()
Ответ на: комментарий от anonymous

> А если в actions-list какой-то код, в т.ч. вредоносный?

Откуда там вредоносный код ? Это defparameter/defvar в сырцах которые пишу я же, и после компиляции в бинарь поменять ничего нельзя.

> Ну и вообще, выглядит как-то излише громоздко.

Ога. Вот это точно выглядит громоздко:

(eval-when (:compile-toplevel :load-toplevel :execute)
  (defparameter *workspace-actions-list* 
	'((aNew "&New" #'new-page-handler "tab-new" "Ctrl+N")
	  (aOpen "&Open..." #'open-page-handler "open" "Ctrl+O")
	  (aSave "&Save" #'save-page-handler "save" nil)
	  (aSaveAs "Save &As..." #'save-as-page-handler nil nil)
	  (aRenamePage "&Rename" #'rename-page-handler nil nil)
	  (aClosePage "&Close" #'close-page-handler "quit" "Ctrl+W"))))

(defun create-workspace-menus (workspace)
...
	(with-new-actions window (workspace-id workspace) *workspace-actions-list*
	  (create-menu page-menu
				   aNew
				   aOpen
				   aSave
				   aSaveAs
				   nil
				   aRenamePage
				   nil
				   aClosePage)

по сравнению с:

(defun create-workspace-menus (workspace)
...
        (let ((aNew (new-action "&New" window))
                  (aOpen (new-action "&Open..." window))
                  (aSave (new-action "&Save" window))
                  (aSaveAs (new-action "Save &As..." window))
                  (aRenamePage (new-action "&Rename..." window))
                  (aClosePage (new-action "&Close" window)))
          (flet ((action-with-data (action handler icon &optional shortcut)
                           (set-action action handler icon :shortcut shortcut)
                           (qfun action "setData" (qnew "QVariant(int)" (workspace-id workspace)))))
                (action-with-data aNew #'new-page-handler "tab-new" "Ctrl+N")
                (action-with-data aOpen #'open-page-handler "open" "Ctrl+O")
                (action-with-data aSave #'save-page-handler "save" "Ctrl+S")
                (action-with-data aSaveAs #'save-as-page-handler nil)
                (action-with-data aRenamePage #'rename-page-handler nil)
                (action-with-data aClosePage #'close-page-handler "quit" "Ctrl+W"))
          (create-menu page-menu
                                   aNew
                                   aOpen
                                   aSave
                                   aSaveAs
                                   nil
                                   aRenamePage
                                   nil
                                   aClosePage)

И так вот в десятке мест.

Darkman ★★★
() автор топика
Ответ на: комментарий от anonymous

> EVAL в CL в 99.999% случаев - непонимание как задачи, так и CL.

Задача - раскрыть глобальную переменную в макросе во время компиляции. Как решить без EVAL ?

Darkman ★★★
() автор топика
Ответ на: комментарий от anonymous

Я бы три девятки тут отрезал. Из оставшихся, заднюю заменил бы на пятёрку.

den73 ★★★★★
()
Ответ на: комментарий от anonymous

> Шаблоны actions во всех этих defun одни и те же?

Нет, естественно.

Darkman ★★★
() автор топика
Ответ на: комментарий от Darkman

Лучше так:

(defun get-action-name-bindings (list parent)
   (mapcar #'(lambda (def-list)
            `(,(first def-list) (new-action ,(second def-list) ,parent)))
            list))

(defun get-action-handlers-bindings (list)
  (mapcar #'(lambda (def-list)
           `(action-with-data (,(first def-list)) ,@(cddr def-list))
            list))
Поскольку ты генерируешь куски кода, логично применить здесь квазицитирование (хотя я мог где-то опечататься, но общая идея должна быть ясна).

den73 ★★★★★
()
Ответ на: комментарий от Darkman

> Вот это как раз и решается eval-when
А также это можно решить на уровне asdf (и это предпочтительно), просто выносишь константы в отдельный файл, который компилируется и загружается до начала компиляции файла с твоими defun-ами. Правда, asdf - это то ещё г. Вроде asdf 2 получше, хотя за её работоспобность я тоже не могу поручиться.

den73 ★★★★★
()
Ответ на: комментарий от Darkman

>Задача - раскрыть глобальную переменную в макросе во время компиляции. Как решить без EVAL ?

Тебе уже намекали... Судя по примеру *workspace-actions-list* - константа, да ещё и времени компиляции (зачем остальные времена?). Тогда используй #.

yyk ★★★★★
()
Ответ на: комментарий от den73

> Потому что лисперы, вместо того, чтобы помочь новичку, чморят его.

Что-то я не заметил там чморения. Хотя у анонимуса ЧСВ сильно повышенное.

anonymous
()
Ответ на: комментарий от den73

Да, с квазицитированием выглядит симпатичнее.

Darkman ★★★
() автор топика
Ответ на: комментарий от Darkman

Задача - раскрыть глобальную переменную в макросе во время компиляции. Как решить без EVAL ?

Сдаётся мне, что здесь можно применить symbol-macrolet hyperspec.

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

anonymous
()
Ответ на: комментарий от yyk

> #.
ПМЛМ, это - хуже, чем eval-when и eval, вместе взятые. Вот её точно нужно использовать только тогда, когда это строго необходимо.

зачем остальные времена?

А вот бывает, что нужно.

den73 ★★★★★
()
Ответ на: комментарий от anonymous

> Сдаётся мне, что здесь можно применить symbol-macrolet hyperspec.
Может, тогда уж глобальный define-symbol-macro. Только на самом деле, я не вижу преимуществ по сравнению с предложенным eval-ом.

den73 ★★★★★
()
Ответ на: комментарий от Darkman

>Можно пример ? Из cltl2 непонятно как его использовать.

замени defparameter на defconstant (не помню - её нужен eval-when или defconstant автоматом во всех временах?) или оставь defparameter но таки оберни в eval-when

тогда

(with-new-actions 'parent #.*workspace-actions-list*)

из первоначального примера должен отработать как ты хочешь

yyk ★★★★★
()
Ответ на: комментарий от den73

>ПМЛМ, это - хуже, чем eval-when и eval, вместе взятые. Вот её точно нужно использовать только тогда, когда это строго необходимо.

Почему? (в данном конкретном случае применительно к константе)

А вот бывает, что нужно.


из примера не видно

yyk ★★★★★
()
Ответ на: комментарий от yyk

> Почему? (в данном конкретном случае применительно к константе)
Во-первых, пока что автор темы ещё не сказал, что это - константа?
Это - переменная времени компиляции. Недаром же она участвует в дефуне в качестве параметра. Может, у него несколько разных меню, и часть из них тоже генерируется динамически или читается из файла.

из примера не видно

Например, для интерактивной разработки. Сгенерил-запустил-поправил.
Или сгенерить какое-то меню в рантайме.

Почему плох #. , я не помню. Может быть, из-за взаимодействия с пакетами. А может быть, просто тем, что к двум уровням, к-рые почти всегда достаточны (compile, load), добавляет третий (read), что ведёт к усложнению анализа процесса загрузки. Например, если автору темы понадоится, чтобы *workspace-actions-list* вычислялся, ему придётся перетаскивать на более ранний этап чтения всё, что нужно для этого вычисления. Если всё это - только ради избавления от eval, то я не вижу смысла. eval - законная часть языка и ей можно пользоваться там, где это уместно. В данном случае - уместно.

den73 ★★★★★
()
Ответ на: комментарий от yyk

> замени defparameter на defconstant
И потом поимей проблемы с переопределением константы. defparameter тут - самое правильное.

den73 ★★★★★
()
Ответ на: комментарий от yyk

> из первоначального примера должен отработать как ты хочешь

Работает. Только непонятно чем вот это:

#.foo is read as the object resulting from the evaluation of the Lisp object represented by foo, which may be the printed representation of any Lisp object.

отличается от eval в макре - и там и там время компиляции.

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