知道你在看什么时必须是一个宏

问题描述 投票:3回答:2

我知道有macro-function,解释here,它允许你检查,但它是否也可以简单地阅读lisp源有时推断你正在看什么“必须是一个宏”? (当然假设您之前从未见过函数/宏)。

我很确定答案是肯定的,但由于这似乎是如此根本,我认为值得一提,特别是因为这方面的任何细微差别可能是有价值的,有趣的。

在Paul Graham的ANSI Common Lisp,p70中,他描述了如何使用defstruct

当我看到(defstruct point x y)时,我是否完全不了解defstruct是什么,这也可能是一个功能。

但是,当我看到

(defstruct polemic
  (subject "foo")
  (effect  "bar"))

我知道必须是一个宏因为(让我们假设),我也知道subjecteffect是未定义的函数。 (我知道因为他们在undefined function被称为'在顶层'时出错(?))(如果那是正确的术语)。

如果引用上面defstruct的两个列表参数,那就不会那么简单了。因为它们没有被引用,所以它必须是一个宏。

它就这么简单吗?

我已经从书中使用的字段略微改变了字段名称,以使这个问题更加清晰。

最后,格雷厄姆写道:

“我们可以通过在原始定义的列表中包含字段名称和默认表达式来指定结构字段的默认值”

我注意到的是,这是真的,但它不是(引用的)列表。这篇文章的任何读者都会以完全不同的方式表达上述句子(鉴于书中尚未介绍宏(尽管我已基本了解它们是什么))。

我的感觉是它不是一个“数据列表”,这些默认表达被包含在内。(对于坏的术语道歉) - 在这里寻求如何正确地概念化。

common-lisp
2个回答
4
投票

一般来说,你是对的:如果在调用中有一些嵌套并且你确定嵌套列表的汽车不是函数 - 它就是一个宏。

而且,几乎总是,def-something和with-something是宏。

但是没有保证。问题是,你想要完成什么?一些代码行走/转换或外部处理(如在编辑器中)。对于后者,您应该记住,只有在执行代码评估时才能进行完全控制,尽管启发式(如在Emacs中)可能会让您走得很远。或者你只是想发展自己的直觉,以便更快地进行代码阅读......


3
投票

有一组约定可以通过模仿现有宏的语法或CL的特殊运算符来识别非常清晰的形式应该是宏。例如,以下是各种虚构宏的混合,但即使不知道它们的定义,代码也不应该太难以理解:

(defun/typed example ((id (integer 0 10)))
  (with-connection (connection (connect id))
    (do-events (event connection)
      (event-case event
        (:quit (&optional code) (return code))))))

关于宏的通常建议是尽可能避免它们,所以如果你发现一些作为lisp表达式没有意义的东西,它可能是,或者被包含在宏中。

(defstruct point x y)

[...]我完全不知道defstruct是什么,这也可能是一个功能。

有各种提示,这不是一个功能。首先,名称以def开头。然后,如果defstruct是一个函数,那么pointxy都将在调用函数之前被评估,这意味着代码将依赖于全局变量,即使它们没有戴耳罩(例如*point**x**y*) ,您可能在前面的表单中(或稍后在同一个编译单元中)找不到它们的任何定义。此外,如果它是一个函数,结果将被直接丢弃,因为它没有被使用(这是一个顶层形式)。这只表明可能存在副作用,但这仍然是不寻常的。具有副作用的顶级函数将改为使用带引号的数据:

(register-struct 'point '(x y))

最后,有些情况下您无法轻易猜出是使用宏还是函数:

(my-get object :slot)

这可能是一个函数调用,或者你可以有一个宏将上面的内容转换为(aref object 0)(假设:slot是对象中的第0个插槽,因为假定所有对象都是由向量支持的某个自定义类型)。您也可以使用编译器宏。如有疑问,请尝试宏展开并查看文档。

热门问题
推荐问题
最新问题