防止递归函数重新分配新变量

时间:2013-07-14 13:13:33

标签: variables recursion common-lisp

我在编程课程中的任务之一是“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棒。

2 个答案:

答案 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))))