Macros SBCL中奇怪的宏扩展错误
我想使用SBCL 1.3.3 for Windows x86_64在Lisp中编写一个Fibonacci数计算函数Macros SBCL中奇怪的宏扩展错误,macros,lisp,common-lisp,sbcl,quote,Macros,Lisp,Common Lisp,Sbcl,Quote,我想使用SBCL 1.3.3 for Windows x86_64在Lisp中编写一个Fibonacci数计算函数fib。使用延迟计算来避免重复。目前的工作守则是: (defvar *fibs* (make-hash-table)) (defun get-value (idx) (if (functionp (gethash idx *fibs*)) (setf (gethash idx *fibs*) (funcall (gethash idx *fibs*)))
fib
。使用延迟计算来避免重复。目前的工作守则是:
(defvar *fibs* (make-hash-table))
(defun get-value (idx)
(if (functionp (gethash idx *fibs*))
(setf (gethash idx *fibs*)
(funcall (gethash idx *fibs*)))
(gethash idx *fibs*)))
(defun fib (n)
(loop for i from 0 below n
if (< i 2) do (setf (gethash i *fibs*) 1)
else do (setf (gethash i *fibs*)
(eval `(lambda () (+ (get-value ,(- i 2))
(get-value ,(- i 1)))))))
(get-value (- n 1)))
但它说:
; in: DEFUN FIB
; (CODE-FOR I)
;
; caught ERROR:
; during macroexpansion of (CODE-FOR I). Use *BREAK-ON-SIGNALS* to intercept.
;
; Argument X is not a NUMBER: I
;
; compilation unit finished
; caught 1 ERROR condition
这很奇怪:我在代码中没有参数X
,而I
总是用作整数
经过一些研究,我发现在宏循环中存在code for
的宏扩展,并且code for
作为符号(?)而不是数字接收I
,这就是问题所在。尽管如此,我仍然不知道为什么代码是错误的,或者如何改进它
2018年4月12日编辑
正如tfb所指出的,解决问题的最佳方法取决于问题是什么。整个问题是向学生解释什么是懒惰评估,如何用Lisp进行评估,以及为什么有必要这样做。斐波那契数不是本例的主要目标
coredump显示了问题的根本原因(宏扩展)和解决方案(i的额外绑定)。不幸的是,这使得所有代码都不适合showcase,因为有太多额外的解释。因此,我最终通过递归更改循环
:
(defvar*fibs*(生成哈希表))
(定义获取值(idx)
(if(functionp(gethash idx*fibs*))
(setf(gethash idx*fibs*)
(funcall(gethash idx*fibs*))
(gethash idx*fibs*))
(除纤(n和可选)(i(-n 1)))
(如果(
[注意coredump的回答解释了宏的错误:我重点讨论了函数的错误以及解决问题的更好方法。]
我不知道你为什么认为你需要这里的EVAL
,或者宏:你可以用LAMBDA
创建一个函数。以下是您的代码的一个有效版本:
(defvar *fibs* (make-hash-table))
(defun get-value (idx &optional (default nil))
;; return the value and whether it was there. If it's a function,
;; call it and stash the result
(multiple-value-bind (got presentp)
(gethash idx *fibs* default)
(values
(typecase got
(function (setf (gethash idx *fibs*)
(funcall got)))
(t got))
presentp)))
(defun fib (n)
(loop for i from 0 below n
if (< i 2) do (setf (gethash i *fibs*) 1)
else do (setf (gethash i *fibs*)
(let ((i i))
;; rebind I as we don't want to depend on whatever
;; LOOP does, which probably is mutate a single
;; binding of I
(lambda () (+ (get-value (- i 2))
(get-value (- i 1)))))))
;; just return the first value as we know the second will be T, and
;; it's not interesting
(values (get-value (- n 1))))
(defvar*fibs*(生成哈希表))
(defun获取值(idx&可选(默认为零))
;返回值以及它是否存在。如果它是函数,
打电话把结果藏起来
(多值绑定(got presentp)
(gethash idx*fibs*默认值)
(价值观
(打字机)
(函数(setf(gethash idx*fibs*))
(所有人都得到了)
(t得到)
演示文稿(P)))
(除纤维(n)
(从n以下0开始的i循环)
if(
但这是一个相当糟糕的方法。相反,您可以只使用显式记忆功能,如下所示:
(defun fibonacci (n)
;; an explicitly-memoized version of the Fibonacci function
(let ((memo (make-hash-table :test #'eql)))
(labels ((fib (m)
(cond
((< m 1)
(error "defined on naturals (excluding 0)"))
((< m 3)
1)
(t
(multiple-value-bind (v p) (gethash m memo)
(if p
v
(setf (gethash m memo) (+ (fib (- m 1))
(fib (- m 2))))))))))
(fib n))))
(定义斐波那契(n)
;斐波那契函数的明确记忆版本
(let((备忘录(制作哈希表:test#'eql)))
标签((fib(m)
(续)
(
更好的办法是,定义一个宏,让您可以记忆任何函数:有一些软件包可以让您这样做,尽管我不确定它们是什么(一个是由于我,但我不确定是否没有更好的,或者实际上是现在找到我的软件包的正确位置!)[请注意,coredump的回答解释了宏的错误:我重点介绍了函数的错误以及解决问题的更好方法。]
我不知道你为什么认为你需要这里的EVAL
,或者宏:你可以用LAMBDA
创建一个函数
(defvar *fibs* (make-hash-table))
(defun get-value (idx &optional (default nil))
;; return the value and whether it was there. If it's a function,
;; call it and stash the result
(multiple-value-bind (got presentp)
(gethash idx *fibs* default)
(values
(typecase got
(function (setf (gethash idx *fibs*)
(funcall got)))
(t got))
presentp)))
(defun fib (n)
(loop for i from 0 below n
if (< i 2) do (setf (gethash i *fibs*) 1)
else do (setf (gethash i *fibs*)
(let ((i i))
;; rebind I as we don't want to depend on whatever
;; LOOP does, which probably is mutate a single
;; binding of I
(lambda () (+ (get-value (- i 2))
(get-value (- i 1)))))))
;; just return the first value as we know the second will be T, and
;; it's not interesting
(values (get-value (- n 1))))
(defvar*fibs*(生成哈希表))
(defun获取值(idx&可选(默认为零))
;返回值以及它是否存在。如果它是函数,
打电话把结果藏起来
(多值绑定(got presentp)
(gethash idx*fibs*默认值)
(价值观
(打字机)
(函数(setf(gethash idx*fibs*))
(所有人都得到了)
(t得到)
演示文稿(P)))
(除纤维(n)
(从n以下0开始的i循环)
if(
但这是一种相当糟糕的方法。相反,您可以只使用一个显式记忆函数,如下所示:
(defun fibonacci (n)
;; an explicitly-memoized version of the Fibonacci function
(let ((memo (make-hash-table :test #'eql)))
(labels ((fib (m)
(cond
((< m 1)
(error "defined on naturals (excluding 0)"))
((< m 3)
1)
(t
(multiple-value-bind (v p) (gethash m memo)
(if p
v
(setf (gethash m memo) (+ (fib (- m 1))
(fib (- m 2))))))))))
(fib n))))
(定义斐波那契(n)
;斐波那契函数的明确记忆版本
(let((备忘录(制作哈希表:test#'eql)))
标签((fib(m)
(续)
((code-for i)
(lambda () (+ (get-value (- i 2))
(get-value (- i 1))))
(let ((i i)) (lambda ...))