Lisp 宏找不到适用的函数

如何解决Lisp 宏找不到适用的函数

给定宏:


(defclass sample-class ()
  ((slot-1  :accessor slot-1
            :initform "sample slot")))

(defvar *sample-instance*(make-instance 'sample-class))

(defmacro sample-macro (p)
  `(if (typep,p 'sample-class)
      (progn
         (print "evaluated")
         (print,(slot-1 p)))))

(sample-macro *sample-instance*)

我很困惑为什么这是错误输出

Execution of a form compiled with errors.
Form:
  (SAMPLE-MACRO *SAMPLE-INSTANCE*)
Compile-time error:
  (during macroexpansion of (SAMPLE-MACRO *SAMPLE-INSTANCE*))
There is no applicable method for the generic function
  #<STANDARD-GENERIC-FUNCTION COMMON-LISP-USER::SLOT-1 (1)>
when called with arguments
  (*SAMPLE-INSTANCE*).
See also:
  The ANSI Standard,Section 7.6.6
   [Condition of type SB-INT:COMPILED-PROGRAM-ERROR]

宏不应该在过程中展开和评估s-form吗?为什么读者找不到泛型函数 slot-1

解决方法

我认为您对宏的作用感到困惑。宏是源代码的转换。所以考虑一下当系统尝试扩展宏表 (sample-macro *sample-instance*) 时会发生什么。在宏扩展时,p符号 *sample-instance*:表示一小段源代码。

现在,查看宏主体中的反引号形式:其中有 ,(slot-1 p):这将尝试在 slot-1 绑定到的任何对象上调用 p,其中是一个符号。然后失败,结果宏展开失败。

好吧,你可以用一种看起来很明显的方式“修复”这个问题:

(defmacro sample-macro (p)
  `(if (typep,p 'sample-class)
      (progn
        (print "evaluated")
        (print (slot-1,p)))))

这似乎有效。使用宏扩展跟踪器:

(sample-macro *sample-instance*)
 -> (if (typep *sample-instance* 'sample-class)
        (progn (print "evaluated") (print (slot-1 *sample-instance*))))

如果您使用宏,它将“起作用”。除了它根本不起作用:考虑这种形式:(sample-macro (make-instance 'sample-class)):好吧,让我们看看使用宏跟踪器:

(sample-macro (make-instance 'sample-class))
 -> (if (typep (make-instance 'sample-class) 'sample-class)
        (progn
          (print "evaluated")
          (print (slot-1 (make-instance 'sample-class)))))

哦,亲爱的。

所以我们可以通过像这样重写宏来解决这个问题:

(defmacro sample-macro (p)
  `(let ((it,p))
     (if (typep it 'sample-class)
      (progn
        (print "evaluated")
        (print (slot-1 it)))

现在

(sample-macro (make-instance 'sample-class))
 -> (let ((it (make-instance 'sample-class)))
      (if (typep it 'sample-class)
          (progn (print "evaluated") (print (slot-1 it)))))

哪个更好。在这种情况下它甚至是安全的,但在大多数情况下,我们需要对我称之为 it 的东西使用 gensym:

(defmacro sample-macro (p)
  (let ((itn (make-symbol "IT")))       ;not needed for this macro
    `(let ((,itn,p))
       (if (typep,itn 'sample-class)
           (progn
             (print "evaluated")
             (print (slot-1,itn)))))))

现在:

(sample-macro (make-instance 'sample-class))
 -> (let ((#:it (make-instance 'sample-class)))
      (if (typep #:it 'sample-class)
          (progn (print "evaluated") (print (slot-1 #:it)))))

所以这个(实际上也是它以前的版本)终于可以工作了。

等等,等等。我们所做的就是把这件事变成这样:

  • 将其参数的值绑定到一个变量;
  • 并使用该绑定评估一些代码。

有一个名字可以用来做这件事,这个名字是函数

(defun not-sample-macro-any-more (it)
  (if (typep it 'sample-class)
      (progn
        (print "evaluated")
        (print (slot-1 it)))))

这完成了 sample-macro 的工作版本所做的一切,但没有所有不必要的复杂性。

嗯,它不会做一件事:它不会内联扩展,也许这意味着它可能会慢一点。

好吧,在以煤为燃料的 Lisp 时代,这是一个真正的问题。燃煤 Lisp 系统具有由木屑和锯末制成的原始编译器,并在确实非常慢的计算机上运行。所以人们会写一些语义上应该是宏的东西,这样木头编译器就会内联代码。有时这甚至是值得的。

但现在我们有了先进的编译器(可能仍然主要由木屑和锯末制成),我们可以说出我们的实际意思:

(declaim (inline not-sample-macro-any-more))

(defun not-sample-macro-any-more (it)
  (if (typep it 'sample-class)
      (progn
        (print "evaluated")
        (print (slot-1 it)))))

现在您可以合理地保证 not-sample-macro-any-more 将被内联编译。

在这种情况下甚至更好(但代价是几乎可以肯定没有内联内容):

(defgeneric not-even-slightly-sample-macro (it)
  (:method (it)
   (declare (ignore it))
   nil))

(defmethod not-even-slightly-sample-macro ((it sample-class))
  (print "evaluated")
  (print (slot-1 it)))

所以这里的总结是:

将宏用于它们的用途,即转换源代码。如果您不想这样做,请使用函数。如果您确定调用函数的行为会占用大量时间,那么请考虑将它们声明为内联以避免这种情况。

,

其他答案解释说,宏执行是关于转换宏扩展时可用的源代码和值。

让我们也试着理解错误信息。我们需要从字面上理解:

Execution of a form compiled with errors.

上面说的是编译。

Form:
  (SAMPLE-MACRO *SAMPLE-INSTANCE*)

以上是要编译的源代码。

Compile-time error:
  (during macroexpansion of (SAMPLE-MACRO *SAMPLE-INSTANCE*))

再次:编译,现在特别是在宏扩展期间。

There is no applicable method for the generic function
  #<STANDARD-GENERIC-FUNCTION COMMON-LISP-USER::SLOT-1 (1)>
when called with arguments
  (*SAMPLE-INSTANCE*).

现在上面是有趣的部分:没有适用于泛型函数 SLOT-1 和参数 *SAMPLE-INSTANCE* 的方法。

什么是*SAMPLE-INSTANCE*这是一个符号。在您的代码中有一个方法,但它用于类 sample-class 的实例。但是没有符号的方法。所以这行不通:

(setf p '*sample-instance*)
(slot-1 p)

这基本上就是你的代码所做的。您希望使用运行时值,但您在编译时得到的只是一个源符号...

显示带有源代码元素的编译时错误的编译器错误消息表明存在运行时和宏扩展时间计算的混淆。

See also:
  The ANSI Standard,Section 7.6.6
   [Condition of type SB-INT:COMPILED-PROGRAM-ERROR]
,

要了解宏的作用,让我们使用 macroexpand

(macroexpand-1 '(sample-macro *sample-instance*))

=>

There is no applicable method for the generic function
  #<STANDARD-GENERIC-FUNCTION COMMON-LISP-USER::SLOT-1 (1)>
when called with arguments
  (*SAMPLE-INSTANCE*).
   [Condition of type SB-PCL::NO-APPLICABLE-METHOD-ERROR]

糟糕,同样的错误信息。我将简化宏并删除 slot-1 周围的评估。

(defmacro sample-macro (p)
  `(if (typep,p 'sample-class)
      (progn
         (print "evaluated")
         (print (slot-1 p)))))

(macroexpand-1 '(sample-macro *sample-instance*))
=>
(IF (TYPEP *SAMPLE-INSTANCE* 'SAMPLE-CLASS)
    (PROGN (PRINT "evaluated") (PRINT (SLOT-1 P))))

代码看起来不错,直到变量 P。那么它可以简单地与,p一起使用吗?无需写 ,(slot-1 p),因为 slot-1 在这里正确。

(defmacro sample-macro (p)
  `(if (typep,p 'sample-class)
      (progn
         (print "evaluated")
         (print (slot-1,p)))))

(macroexpand-1 '(sample-macro *sample-instance*))
=>
(IF (TYPEP *SAMPLE-INSTANCE* 'SAMPLE-CLASS)
    (PROGN (PRINT "evaluated") (PRINT (SLOT-1 *SAMPLE-INSTANCE*))))

代码看起来是正确的。

(sample-macro *sample-instance*)

"evaluated" 
"sample slot" 

它有效。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res