Re: how to debng
Haris Bogdanović <fbogdanovic@xnet.hr> writes:
> Hi.
>
> I have this sample function:
>
> (defun init ()
> (defvar *pics*)
> (defvar *pics-temp*)
> (setf *pics* '("pic1" "pic2" "pic3" "pic4"))
> (setf *pics-temp* ())
> (let ((len (length *pics*)))
> (dotimes (i len)
> (let ((x (random (- len i))))
> (push (nth x *pics*) *pics-temp*)
> (delete-if (constantly t) *pics* :start x :end (1+ x))))))
>
> I want to see what is happening in function.
> I know how to set breakpoint and enter stepper after
> calling function from listener.
> How can I see variables or forms I choose, changing values
> (in one window) while going through stepper ?
Even before that, you should try to write it more properly.
(defvar *pics* '("pic1" "pic2" "pic3" "pic4")
"A list of strings naming 'pics'.")
(defvar *pics-temp* '()
"A global temporary? list of 'pic' name.")
(defun init ()
(let ((len (length *pics*)))
(dotimes (i len)
(let ((x (random (- len i))))
(push (nth x *pics*) *pics-temp*)
(setf *pics* (remove-if (constantly t) *pics* :start x :end (1+ x)))))))
Since *pics* is bound to a literal list, that list cannot be
destructively modified, so delete-if is out of the question.
Instead of stepping, IMO it would be easier to see the behavior of the
function if you printed some choosen variable during the workings of
the function.
(defun init ()
(let ((len (length *pics*)))
(dotimes (i len)
(let ((x (random (- len i))))
(show *pics* *pics-temp* len x)
(push (nth x *pics*) *pics-temp*)
(setf *pics* (remove-if (constantly t) *pics* :start x :end (1+ x)))))
(show *pics* *pics-temp*)
nil))
;; The show macro is in http://www.informatimago.com/develop/lisp/com/informatimago/common-lisp/interactive.lisp
CL-USER> (init)
*PICS* = ("pic1" "pic2" "pic3" "pic4")
*PICS-TEMP* = NIL
LEN = 4
X = 2
*PICS* = ("pic1" "pic2" "pic4")
*PICS-TEMP* = ("pic3")
LEN = 4
X = 0
*PICS* = ("pic2" "pic4")
*PICS-TEMP* = ("pic1" "pic3")
LEN = 4
X = 0
*PICS* = ("pic4")
*PICS-TEMP* = ("pic2" "pic1" "pic3")
LEN = 4
X = 0
*PICS* = NIL
*PICS-TEMP* = ("pic4" "pic2" "pic1" "pic3")
NIL
Now that function is still bad since it's O(n*(2n)), since nth and
remove-if are O(n). Another thing that doesn't feel right, is this
spread between the immutable original list, and the modification of
*pics* (either destructively with delete-if, or not destructively with
remove-if, since in the later case it means copying and copying the
list over). If you could choose one way or the other we could write a
more elegant function. Finally, the use of global variable is always
wrong, and the name you chosed don't seem to indicate they've got any
justification.
For example, if you chosed to implement a destructive list shuffle
function, you could write:
(defun nshuffle-list (list)
(loop
:with wrapped = (cons nil list)
:for length :from (length list) :above 0
:collect (pop (cdr (nthcdr (random length) wrapped)))))
NSHUFFLE-LIST
CL-USER> (nshuffle-list (list "pic1" "pic2" "pic3" "pic4"))
("pic1" "pic4" "pic3" "pic2")
CL-USER> (nshuffle-list (list "pic1" "pic2" "pic3" "pic4"))
("pic2" "pic1" "pic4" "pic3")
CL-USER>
and to see how it works:
(defun nshuffle-list (list)
(loop
:with wrapped = (cons nil list)
:for length :from (length list) :above 0
:for r = (random length)
:do (show wrapped result length r (nthcdr r wrapped))
:collect (pop (cdr (nthcdr r wrapped))) :into result
:finally (return result)))
NSHUFFLE-LIST
CL-USER> (nshuffle-list (list "pic1" "pic2" "pic3" "pic4"))
WRAPPED = (NIL "pic1" "pic2" "pic3" "pic4")
RESULT = NIL
LENGTH = 4
R = 2
(NTHCDR R WRAPPED) = ("pic2" "pic3" "pic4")
WRAPPED = (NIL "pic1" "pic2" "pic4")
RESULT = ("pic3")
LENGTH = 3
R = 0
(NTHCDR R WRAPPED) = (NIL "pic1" "pic2" "pic4")
WRAPPED = (NIL "pic2" "pic4")
RESULT = ("pic3" "pic1")
LENGTH = 2
R = 1
(NTHCDR R WRAPPED) = ("pic2" "pic4")
WRAPPED = (NIL "pic2")
RESULT = ("pic3" "pic1" "pic4")
LENGTH = 1
R = 0
(NTHCDR R WRAPPED) = (NIL "pic2")
("pic3" "pic1" "pic4" "pic2")
CL-USER> (nshuffle-list (list "pic1" "pic2" "pic3" "pic4"))
WRAPPED = (NIL "pic1" "pic2" "pic3" "pic4")
RESULT = NIL
LENGTH = 4
R = 3
(NTHCDR R WRAPPED) = ("pic3" "pic4")
WRAPPED = (NIL "pic1" "pic2" "pic3")
RESULT = ("pic4")
LENGTH = 3
R = 1
(NTHCDR R WRAPPED) = ("pic1" "pic2" "pic3")
WRAPPED = (NIL "pic1" "pic3")
RESULT = ("pic4" "pic2")
LENGTH = 2
R = 0
(NTHCDR R WRAPPED) = (NIL "pic1" "pic3")
WRAPPED = (NIL "pic3")
RESULT = ("pic4" "pic2" "pic1")
LENGTH = 1
R = 0
(NTHCDR R WRAPPED) = (NIL "pic3")
("pic4" "pic2" "pic1" "pic3")
CL-USER>
Notice that you have to feed it a new list everytime. You could even
write it so that the cons cells provided in by list argument are
actually reused in the result:
(defun nshuffle-list (list)
(loop
:with result = '()
:with wrapped = (cons nil list)
:for length :from (length list) :above 0
:for previous = (nthcdr (random length) wrapped)
:for cell = (cdr previous)
:for rest = (cdr cell)
:do (setf (cdr cell) result
result cell
(cdr previous) rest)
:finally (return result)))
CL-USER> (let ((list (list "pic1" "pic2" "pic3" "pic4")))
(show (nshuffle-list list) list))
(NSHUFFLE-LIST LIST) = ("pic2" "pic3" "pic1" "pic4")
LIST = ("pic1" "pic4")
("pic1" "pic4")
CL-USER> (let ((list (list "pic1" "pic2" "pic3" "pic4")))
(show (nshuffle-list list) list))
(NSHUFFLE-LIST LIST) = ("pic2" "pic4" "pic1" "pic3")
LIST = ("pic1" "pic3")
("pic1" "pic3")
On the other hand, if you went to the functional paradigm, you could
write a function that doesn't modify it's argument:
(defun shuffle-list (list)
(nshuffle-list (copy-list list)))
so that you can apply it on literal lists:
CL-USER> (let ((list '("pic1" "pic2" "pic3" "pic4")))
(show (shuffle-list list) list))
(SHUFFLE-LIST LIST) = ("pic3" "pic4" "pic1" "pic2")
LIST = ("pic1" "pic2" "pic3" "pic4")
("pic1" "pic2" "pic3" "pic4")
CL-USER> (let ((list '("pic1" "pic2" "pic3" "pic4")))
(show (shuffle-list list) list))
(SHUFFLE-LIST LIST) = ("pic4" "pic3" "pic2" "pic1")
LIST = ("pic1" "pic2" "pic3" "pic4")
("pic1" "pic2" "pic3" "pic4")
But this is a little cheating, and costly to reuse the destructive
version this way, but the alternative would be to cons a lot of
temporary lists.
Anyways, this would let you write init as:
(defun init ()
(setf *pics-temp* (shuffle-list *pics*)))
and I guess now it's clear how it behave and you don't have to step it anymore.
--
__Pascal Bourguignon__ http://www.informatimago.com/