Reputation: 3055
One of my task in programming class is "Tower Of Hanoi" , the language I was using is Common Lisp and the source code is as follow :
The Code :
Variables:
(defparameter *Source* "Peg 1")
(defparameter *Spare* "Peg 2")
(defparameter *Destination* "Peg 3")
I want the above variable declaraction to be inside the function
(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 "")))))
The Question :
1.)I'm using recursive algorithm to solve this problem , as I know in this algorithm , the 3 variables (Source , Spare and Destination) must interchange with each other (by some rules) . If I place the defvar
inside the function , even though I carry out this 3 operations (setq tempPeg *Spare*)
(setq *Spare* *Destination*)
(setq *Destination* tempPeg)
before calling the towers-of-hanoi function again but the function again redefine the 3 variables back through it's original value .
2.)What I wanted to know is that is it possible for me to place the declaraction of the 3 variables inside the function and still able to prevent the function from redefining the same variable for each recursive called?
P/S the assignment only allows me to define a function header that accept disks as the one and only argument but not the Source , Spare , and Destination rod.
Upvotes: 0
Views: 201
Reputation: 139261
Your use of lists is not idiomatic. Remember lists in Lisp are singly linked cons cells. All operations which traverse lists or work from the end of lists are inefficient.
To answer your question:
(defun do-something (a)
(let ((foo 42)) ; binding
(labels ((do-something-recursively (b) ; local recursive function
(...)))
(do-something-recursively a))))
Upvotes: 1
Reputation: 85853
There are probably two good options here. The first is that since the function depends on a few values, the function could take them as arguments. That's probably the clearest way to do what you're trying to do, and it makes the recursive calls cleaner, because you don't need to rebind or assign a bunch of variables before making the call. For instance, here's a simple recursive function:
(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
Now, if that's supposed to start with some reasonable default values, then those function arguments could be made optional:
(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))))
and then you can simply call (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)
It should be clear how this approach could be applied to the Hanoi problem; you'd simply add three optional arguments to the hanoi
and recursively call it with the altered values:
(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
...)
That said, sometimes there are enough parameters that it's easier to just use special variables (i.e., dynamically scoped variables) for them (though I don't think that this is such a case), and to get those, you can use the 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)))
...))))
You'll still have to establish the initial bindings of the variables, though, so for the outermost call you'd have to do something like:
(defun hanoi-driver (disks)
(let ((*source* "Peg 1")
(*spare* "Peg 2")
(*destination* "Peg 3"))
(declare (special *source* *spare* *destination*))
(hanoi disks)))
I think that that simply adding the three &optional
variables to hanoi
is ultimately a cleaner solution, personally.
Upvotes: 2