我在编程课程中的任务之一是“Tower Of Hanoi”,我使用的语言是Common Lisp,源代码如下:
变量:
(defparameter *Source* "Peg 1")
(defparameter *Spare* "Peg 2")
(defparameter *Destination* "Peg 3")
我希望上面的变量声明在函数
中(defun towers-of-hanoi (disks)
;disks accept list as parameter , for e.g `(s m l)
(let ((tempPeg))
(if (= (list-length disks) 1)
(format t "Move ~{~A~} from ~A to ~A~%"
(last disks) *Source* *Destination*)
(progn
(setq tempPeg *Spare*)
(setq *Spare* *Destination*)
(setq *Destination* tempPeg)
(towers-of-hanoi (subseq disks 0 (- (list-length disks) 1)))
(setq tempPeg *Spare*)
(setq *Spare* *Destination*)
(setq *Destination* tempPeg)
(format t "Move ~{~A~} from ~A to ~A~%"
(last disks) *Source* *Destination*)
(setq tempPeg *Spare*)
(setq *Spare* *Source*)
(setq *Source* tempPeg)
(towers-of-hanoi (subseq disks 0 (- (list-length disks) 1)))
(setq tempPeg *Spare*)
(setq *Spare* *Source*)
(setq *Source* tempPeg)
(format t "")))))
1.)我正在使用递归算法来解决这个问题,正如我在这个算法中所知,3个变量(Source,Spare和Destination)必须相互交换(通过一些规则)。如果我将defvar
置于函数内部,即使我再次执行这3个操作(setq tempPeg *Spare*)
(setq *Spare* *Destination*)
(setq *Destination* tempPeg)
,然后再次调用函数塔功能通过它的原始值重新定义3个变量。
2.)我想知道的是,我是否可以将3个变量的声明放在函数中,并且仍然能够阻止函数为每个被调用的递归重新定义相同的变量?
P / S分配只允许我定义一个函数头,它接受磁盘作为唯一参数,但不接受Source,Spare和Destination棒。
答案 0 :(得分:2)
这里可能有两个不错的选择。第一个是因为函数依赖于几个值,所以函数可以将它们作为参数。这可能是最清晰的方式来做你正在尝试做的事情,并且它使递归调用变得更清晰,因为在进行调用之前你不需要重新绑定或分配一堆变量。例如,这是一个简单的递归函数:
(defun swap-until-x-is-zero (x y)
(print `(swap-until-zero ,x ,y))
(unless (zerop x)
(swap-until-x-is-zero (1- y) (1- x))))
CL-USER> (swap-until-x-is-zero 3 5)
(SWAP-UNTIL-ZERO 3 5)
(SWAP-UNTIL-ZERO 4 2)
(SWAP-UNTIL-ZERO 1 3)
(SWAP-UNTIL-ZERO 2 0)
(SWAP-UNTIL-ZERO -1 1)
(SWAP-UNTIL-ZERO 0 -2)
NIL
现在,如果那应该从一些合理的默认值开始,那么这些函数参数可以是可选的:
(defun swap-until-x-is-zero (&optional (x 3) (y 5))
(print `(swap-until-zero ,x ,y))
(unless (zerop x)
(swap-until-x-is-zero (1- y) (1- x))))
然后您只需致电(swap-until-x-is-zero)
:
CL-USER> (swap-until-x-is-zero)
(SWAP-UNTIL-ZERO 3 5)
(SWAP-UNTIL-ZERO 4 2)
(SWAP-UNTIL-ZERO 1 3)
(SWAP-UNTIL-ZERO 2 0)
(SWAP-UNTIL-ZERO -1 1)
(SWAP-UNTIL-ZERO 0 -2)
应该清楚这种方法如何应用于河内问题;你只需要向hanoi
添加三个可选参数,并以改变的值递归调用它:
(defun towers-of-hanoi (disks &optional (source "Peg 1") (spare "Peg 2") (destination "Peg 3"))
...
;; instead of:
;; (progn
;; (setq tempPeg *Spare*)
;; (setq *Spare* *Destination*)
;; (setq *Destination* tempPeg)
;; (towers-of-hanoi (subseq disks 0 (- (list-length disks) 1))))
;; we do:
(let ((tempPeg spare))
(towers-of-hanoi (subseq disks 0 (- (list-length disks) 1))
source ; stays the same
destination ; swap destination and spare
spare)) ; swap destination and spare
...)
也就是说,有时候有足够的参数可以更容易地为它们使用特殊的变量(即动态范围的变量)(虽然我不认为这是这种情况),为了得到这些,你可以使用special
declaration:
(defun towers-of-hanoi (disks)
(declare (special *source* *spare* *destination*))
(let ((tempPeg))
(if (= (list-length disks) 1)
(format t "Move ~{~A~} from ~A to ~A~%" (last disks) *Source* *Destination*)
(progn
(setq tempPeg *Spare*)
(setq *Spare* *Destination*)
(setq *Destination* tempPeg)
(towers-of-hanoi (subseq disks 0 (- (list-length disks) 1)))
...))))
但是,您仍然需要建立变量的初始绑定,因此对于最外层的调用,您必须执行以下操作:
(defun hanoi-driver (disks)
(let ((*source* "Peg 1")
(*spare* "Peg 2")
(*destination* "Peg 3"))
(declare (special *source* *spare* *destination*))
(hanoi disks)))
我认为,简单地将三个&optional
变量添加到hanoi
最终会成为一个更清晰的解决方案。
答案 1 :(得分:1)
您对列表的使用不是惯用的。记住Lisp中的列表是单独链接的cons细胞。遍历列表或从列表末尾开始工作的所有操作都是低效的。
回答你的问题:
(defun do-something (a)
(let ((foo 42)) ; binding
(labels ((do-something-recursively (b) ; local recursive function
(...)))
(do-something-recursively a))))