宏中的动态lambda参数列表
背景
我通常要撰写多个函数调用,第一个函数要求将lambda函数作为参数传递。我想将最常见的用例包装在宏中,在动态创建Lambda功能的Lambda列表方面遇到麻烦。有一个以前的讨论< /a>,但在这种情况下没有任何帮助。
目的是像这个示例一样调用宏: (过滤器数据(and(String = Origin“ USA”))(&GT;加速10))
其中Origin
和加速
是数据中的列。
当前的宏
(defmacro filter (data &body body)
(with-unique-names (variables predicate)
`(let* ((,variables (key-list ,data ',@body))
1. (,predicate (lambda ,variables ,@body)) ;not working
2. ;; (,predicate (lambda (origin acceleration) ,@body)) ; works
3. (,predicate (eval (read-from-string
(format nil "#.(lambda ~A ~A)"
,variables ',@body)))
(choose ,data
(mask ,data ,variables ,predicate)
t))))
在此示例中,键列表
是一个函数获取数据和主体,并产生包含谓词中所有变量的列表。例如:
CL-USER> (key-list vgcars '(and (string= origin "USA") (> acceleration 10)))
(ORIGIN ACCELERATION)
我已经确认它是列表,并且正确。
在中让
表格:
- 是应该 work
- 做工作,但要求我手工编码lambda的参数列表,这将是不同的对于每个谓词查询。
执行宏会产生编译时错误,以执行宏观错误:
Compile-time error:
The lambda expression has a missing or non-list lambda list:
(LAMBDA #:VARIABLES1199 (AND (STRING= ORIGIN "USA") (> ACCELERATION 10)))
请注意,,变量>的替换缺乏替代。我已经打印并键入检查
,变量
,它 is 具有正确值的列表。同样,bask
要求变量以类似的方式传递,并且在lambda函数的硬编码版本中,此替代起作用。我尝试了参数列表的各种变体,例如(,@variables)等,但无效。
该问题似乎是特定于通过宏替换创建Lambda函数的参数列表。
有人有什么想法吗?
编辑 基于评论,我使用eval
添加了版本3。这确实有效,但需要自动逃脱报价。评估这样的代码存在风险,但是由于打算从repl中调用,因此没有比已经存在的风险更大。
Background
I'm typically composing several function calls, the first of which requires a lambda function to be passed as an argument. I want to wrap the most common use cases with a macro, am having trouble with dynamically creating the lambda list for the lambda function. There was one previous discussion, but nothing there helps in this situation.
The intention is to call the macro like this example:(filter data (and (string= origin "USA") (> acceleration 10)))
where origin
and acceleration
are columns in the data.
Current macro
(defmacro filter (data &body body)
(with-unique-names (variables predicate)
`(let* ((,variables (key-list ,data ',@body))
1. (,predicate (lambda ,variables ,@body)) ;not working
2. ;; (,predicate (lambda (origin acceleration) ,@body)) ; works
3. (,predicate (eval (read-from-string
(format nil "#.(lambda ~A ~A)"
,variables ',@body)))
(choose ,data
(mask ,data ,variables ,predicate)
t))))
In this example, key-list
is a function takes the data and body and produces a list containing all the variables in the predicate. For example:
CL-USER> (key-list vgcars '(and (string= origin "USA") (> acceleration 10)))
(ORIGIN ACCELERATION)
I've confirmed that it is a list, and correct.
In the let
form:
- is what should work
- does work, but requires me to hand-code the lambda's parameter list, and this will be different for every predicate query.
Executing the macro as it is produces a compile time error:
Compile-time error:
The lambda expression has a missing or non-list lambda list:
(LAMBDA #:VARIABLES1199 (AND (STRING= ORIGIN "USA") (> ACCELERATION 10)))
Note the lack of substitution for ,variables
. I've printed and type checked ,variables
and it is a list with the correct values. As well, mask
requires the variables to be passed in a similar manner, and in the hard-coded version of the lambda function this substitution works. I've tried all kinds of variations for the parameter list, e.g. (,@variables), etc., but none work.
The problem seems to be specific to creating the parameter list of the lambda function via macro substitution.
Anyone have any ideas?
Edit
Based on a comment, I've added a version 3, using eval
. This does work but requires escaping quote automatically. There is a risk in evaluating code like this, but since it's intended to be invoked from the REPL, no more risky than what already exists.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
编写宏时需要应用一些规则。这里提到的一些人::
了解宏是代码生成器,并且看不到运行时数据
没有像函数那样命名宏。宏应以不同的方式命名。功能名称处于活动状态:打印,附加,过滤器,...宏名称应使用过滤器,过滤,...
内部的宏定义清楚地说明了宏的变量是什么。不要使用变量,而是变量符号。变量的值不是变量,而是符号。使用不同的命名来用于变量和宏扩展阶段的功能。
清楚地分开并记录宏扩展阶段的变量和功能。
明确分开生成代码和/或返回的代码
用宏expand和macroexpand -1-&gt;检查宏扩展
检查现在查看您的第一个版本的
:)
现在是另一个变体
:)
,然后呼叫:
当宏运行时:
数据是
data
bodybody as
(and(string = Origin) ”)(&gt;加速10))
变量是
foo100
prepicates is
foo200
然后您返回回语列表。回调的列表包含静态数据和数据的组合,这些数据和数据在宏扩展时间进行了评估。在宏膨胀时间评估了前面逗号的每个表达式, rets在宏扩展时间均未评估。
不在宏扩展时间运行:
不在宏扩展时间运行:
这意味着您有很多混乱:
通过with-uinque-names在宏扩展时间引入的新名称
在宏扩展时间运行的一些代码在宏扩展时间引入的新名称,而不是在其后面的系统
运行时评估,即使从文本中阅读
也是如此
通常编写的宏:
这就是这样的:这是这样的:
因为我现在没有掩码或选择的内容,这只是猜测如何为它们生成形式的猜测。
但最大的问题是:密钥列表是在宏扩展时间运行的
您确实需要思考这一点,为什么要通过宏生成代码以及如何生成代码。宏在编译时间运行,然后看不到运行时值。如果变量列表仅在运行时可用,则宏无法生成一个需要这些变量的函数的代码。然后,需要在运行时计算和编译该功能。
There are a few rules you need to apply when writing macros. Some mentioned here::
understand that a macro is a code generator and does not see runtime data
don't name a macro like a function. A macro should be named differently. Function names are active: print, append, filter, ... Macro names should be WITH-FILTER, FILTERING, ...
inside a macro definition make clear what a variable of the macro is. Don't use VARIABLES, but VARIABLES-SYMBOL. The value of VARIABLES is not the VARIABLES, but a SYMBOL. Use different namings for variables and functions of the macro-expansion phase.
clearly separate and DOCUMENT what are variables and functions of the macro expansion phase are.
clearly separate where code is generated and/or returned
test your macro with MACROEXPAND and MACROEXPAND-1 -> check the macro expansion
Now looking at your first version:
)
Now the other variant:
)
And you call:
When the macro runs:
DATA is
DATA
BODY is
(AND (STRING= ORIGIN "USA") (> ACCELERATION 10))
VARIABLES is
FOO100
PREDICATES is
FOO200
Then you return a backquoted list. The backquoted list contains a mix of static data and data which is evaluated at macroexpansion time. Every expression with a comma in front is evaluated at macro expansion time, the rest is not evaluated at macro expansion time.
Not run at macro expansion time:
Not run at macro expansion time:
That means you have a lot of confusion:
new names introduced at macro-expansion time via WITH-UINQUE-NAMES
some code running at macro expansion time and some not, without systematic behind it
runtime evaluation, even with reading from text
A macro typically written:
That would then look like this:
Since I don't now what MASK or CHOOSE does, this is just a guess how to generate forms for them.
BUT THE BIG PROBLEM: KEY-LIST is run at macro expansion time
You really need to think this through, why you want code to be generated by a macro and how. MACROS run at COMPILE TIME and then can't see RUNTIME values. If a list of variables is only available at runtime, then the macro can't generate the code for a function, which needs these variables. The function then needs to be computed and compiled at runtime.
应该很明显,没有这样的事情可以起作用。更普遍地说,我认为您可能对宏的目的感到困惑:请参阅 Rainer Joswig的答案有关此指南的答案。
考虑到这种情况
d
在运行程序后才知道,因此,直到那个时候才知道d
中的任何命名列。但是,如果filter
是一个宏,则在此之前将其扩展,因此在此之前创建了lambda
表达式表达式是在此之前创建的必须在此之前知道其论点。因此,宏不能知道数据中列的名称。再次记住,通用LISP中的宏是其参数是源代码,其值为源代码。宏。在程序运行时仅知道的事物。
(当然,常见的LISP也是一种语言,可以为您提供
eval
&amp;相关的事物,您可以在脖子上吹腿,但不这样做 :为了重新利用JWZ的著名报价:答案是不使用
eval
,而是重新考虑问题。例如:让我们假设您拥有的任何数据都在某种形式的编号列中使用的字段。也许有一个column-ref
访问者。拥有数据(SO:之后,宏观扩展时间)后,您可以从中计算出与列相对应的字段的名称。因此,例如,您可以构造某种表格从字段名称到列号的映射:
现在您可以编写
键ref
conscotsors:现在,现在使用这些表格,您可以编写此宏:
因此现在您可以使用以下内容:
将其扩展到:
请注意,此宏:
symbol-macrolet
的变量,因为它被告知字段名称;eval
。显然,这太过了:大概数据有行之类的东西或其他所有内容是不值得的。无论答案是什么,都不是不是使用
eval
。It should be obvious that nothing like this can work. More generally I think you are probably confused about what macros are for: see Rainer Joswig's answer for some guidelines about that.
Consider this case
d
is not known until the program is run, and hence any named columns ind
are not known until that time. But iffilter
is a macro then it is expanded before that time and hence thelambda
expression it creates is created before that time and hence the names of its arguments must be known before that time. Thus the macro can't know the names of the columns in the data.Yet again remember that macros in Common Lisp are functions whose arguments are source code and whose values are source code. Macros, therefore, are called before the program runs and can not depend on things which are known only when the program runs.
(Of course Common Lisp is also a language which provides you
eval
& related things, by which you can blow your legs off at the neck, but don't do that: to repurpose a famous quote due to jwz:The answer is not to use
eval
but to rethink the problem. As an example: let's assume that whatever data you have has fields which live in numbered columns of some kind. And maybe there's acolumn-ref
accessor for that.Once you have the data (so: properly after macroexpansion time) you can compute from it the names of fields which correspond to columns. So, for instance, you could construct some kind of table which maps from field names to column numbers:
And now you can write
key-ref
accessors:And now, using these you can write this macro:
So now you can use this:
Which expands into:
Note that this macro:
symbol-macrolet
, which it can do since it is told the field names;eval
.Obviously this is oversimplified: presumably the data has rows or something or all this would not be worth it. Whatever the answer is, it is not to use
eval
.