Stack access--Am I looking for continuations?

  • Follow


I'm writing a recursive function, and I find myself asking, how many
invocations of this function are currently on the stack?  What's the
value of a local variable 5 invocations down the stack? I could give
the function an extra argument that contained a list of states the
function had gone through, but this seems wrong:

(defun f (args &optional history)
  ...
    (setf local-state (make-hash-table))
    ...
    (f (cdr args) (cons local-state history))


Is there a better way to do this in Lisp? Is this something
continuations do? If there's a resource I should read, I would
appreciate a pointer.

Ari.

-- 
Elections only count as free and trials as fair if you can lose money
betting on the outcome.
0
Reply ari (61) 7/8/2007 11:29:17 PM

Ari Krupnik wrote:
> I'm writing a recursive function, and I find myself asking, how many
> invocations of this function are currently on the stack?  What's the
> value of a local variable 5 invocations down the stack? I could give
> the function an extra argument that contained a list of states the
> function had gone through, but this seems wrong:
> 
> (defun f (args &optional history)
>   ...
>     (setf local-state (make-hash-table))
>     ...
>     (f (cdr args) (cons local-state history))
> 
> 
> Is there a better way to do this in Lisp? Is this something
> continuations do? If there's a resource I should read, I would
> appreciate a pointer.

As with most things, how one goes about doing something probably depends 
on what it is that one wants to do.  For example, I'm guessing that 
something like the following is not really what you're looking for.  Or 
is it?

CL-USER 21 > (defun f (n)
               (when (= n 0)
                 (break))
               (f (1- n)))
F

CL-USER 22 > (f 5)

Break.
   1 (continue) Return from break.
   2 (abort) Return to level 0.
   3 Return to top loop level 0.

Type :b for backtrace, :c <option number> to proceed,  or :? for other 
options

CL-USER 23 : 1 > :b
Interpreted call to F
Interpreted call to F
Interpreted call to F
Interpreted call to F
Interpreted call to F
Interpreted call to F
Call to EVAL
Call to CAPI::CAPI-TOP-LEVEL-FUNCTION
Call to CAPI::INTERACTIVE-PANE-TOP-LOOP
Call to (SUBFUNCTION MP::PROCESS-SG-FUNCTION MP::INITIALIZE-PROCESS-STACK)

CL-USER 24 : 1 > :v
Interpreted call to F:
   N : 0

CL-USER 25 : 1 > :n
Interpreted call to F

CL-USER 26 : 1 > :n
Interpreted call to F

CL-USER 27 : 1 > :n
Interpreted call to F

CL-USER 28 : 1 > :v
Interpreted call to F:
   N : 3

CL-USER 29 : 1 > :c 4
No such restart.
NIL

CL-USER 30 : 1 > :?

:ed      Edit the function associated with the frame
:v       Print the current frame
:bq      Print a quick backtrace of interesting call frames
:b       Print a backtrace down from the current frame
:error   Print the error and how to continue
:n       Go down the stack
:p       Go up the stack
:top     Abort to top level
:a       Abort one level
:c       Continue from error
:ret     Return from frame
:res     Restart frame
:sres    Restart frame, stepping the function
:trap    Cause the debugger to be re-entered on exit from this frame
:<       Go to the top of the stack
:>       Go to the bottom of the stack
:cc      Get the current condition object
:all     Set the debugger options to show all call frames
:l       Print and return the value of a given variable in the current 
frame.
:func    Get the function ovject in the current frame
:bb      Print a full backtrace suitable for a bug report
:lambda  Show the lambda expression for an anonymous interepreted frame
:bug-form <subject> &key <filename>
          Print out a bug report form, optionally to a file.
:get <variable> <command identifier>
          Get a command from the history list and put it in a variable.
:help    Produce this list.
:his &optional <n1> <n2>
          List the command history, optionally the last n1 or range n1 
to n2.
:redo &optional <command identifier>
          Redo a previous command, identified by its number or a substring.
:use <new form> <old form> &optional <command identifier>
          Redo command after replacing old form with new form.
NIL

CL-USER 30 : 1 > :c 3

CL-USER 31 >
0
Reply dkixk (148) 7/9/2007 1:12:28 AM


Damien Kick <dkixk@earthlink.net> writes:

> Ari Krupnik wrote:
>> I'm writing a recursive function, and I find myself asking, how many
>> invocations of this function are currently on the stack?  What's the
>> value of a local variable 5 invocations down the stack? I could give
>> the function an extra argument that contained a list of states the
>> function had gone through, but this seems wrong:
>>
>> (defun f (args &optional history)
>>   ...
>>     (setf local-state (make-hash-table))
>>     ...
>>     (f (cdr args) (cons local-state history))
>
> As with most things, how one goes about doing something probably
> depends on what it is that one wants to do.  For example, I'm guessing
> that something like the following is not really what you're looking
> for.  Or is it?
>
> CL-USER 21 > (defun f (n)
>               (when (= n 0)
>                 (break))
>               (f (1- n)))
> F

I was looking for something I could do from inside the function, not
the REPL. In the example I gave, I can find out how deep the recursion
is by looking at (length history), for example.

Ari

-- 
Elections only count as free and trials as fair if you can lose money
betting on the outcome.
0
Reply ari (61) 7/10/2007 12:17:01 AM

In article <86d4z2scdu.fsf@deb.lib.aero.>, Ari Krupnik <ari@lib.aero> 
wrote:

> I'm writing a recursive function, and I find myself asking, how many
> invocations of this function are currently on the stack?  What's the
> value of a local variable 5 invocations down the stack? I could give
> the function an extra argument that contained a list of states the
> function had gone through, but this seems wrong:
> 
> (defun f (args &optional history)
>   ...
>     (setf local-state (make-hash-table))

You should use LET to create a local variable, not assign a global 
variable.

>     ...
>     (f (cdr args) (cons local-state history))
> 
> 
> Is there a better way to do this in Lisp? Is this something
> continuations do? If there's a resource I should read, I would
> appreciate a pointer.

This doesn't seem like a common programming pattern, so I don't think 
there's any built-in mechanism or well-known idiom.  There's no standard 
way to look things up in a particular stack frame -- you have to provide 
your own way to access it.

Your hash-table approach requires you to explicitly copy the value of 
each variable into the hash table.  Instead, you could use a local 
function to access the variables you care about:

   (flet ((get-local-state (var)
             (ecase var
               (var1 var1)
               (var2 var2)
               ...)))
     (f (cdr args) (cons #'get-local-state history)))

-- 
Barry Margolin, barmar@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
*** PLEASE don't copy me on replies, I'll read them in the group ***
0
Reply barmar (5664) 7/10/2007 12:57:29 AM

Barry Margolin <barmar@alum.mit.edu> writes:

> In article <86d4z2scdu.fsf@deb.lib.aero.>, Ari Krupnik <ari@lib.aero> 
> wrote:

>> (defun f (args &optional history)
>>   ...
>>     (setf local-state (make-hash-table))
>
> You should use LET to create a local variable, not assign a global 
> variable.

Point taken.

> Your hash-table approach requires you to explicitly copy the value of 
> each variable into the hash table.  Instead, you could use a local 
> function to access the variables you care about:
>
>    (flet ((get-local-state (var)
>              (ecase var
>                (var1 var1)
>                (var2 var2)
>                ...)))
>      (f (cdr args) (cons #'get-local-state history)))

This looks very powerful, much more useful then the hash table I
imagined. But I'm confused--shouldn't the keys be quoted? Otherwise,
it seems to me the values of the local variables will become the keys,
not their names:
                ('var1 var1)
                ('var2 var2)


Ari.

-- 
Elections only count as free and trials as fair if you can lose money
betting on the outcome.
0
Reply ari (61) 7/10/2007 3:13:32 AM

Ari Krupnik wrote:
> Damien Kick <dkixk@earthlink.net> writes:
> 
>> Ari Krupnik wrote:
>>> I'm writing a recursive function, and I find myself asking, how many
>>> invocations of this function are currently on the stack?  What's the
>>> value of a local variable 5 invocations down the stack?
>>
>>                                       [...] For example, I'm guessing
>> that something like the following is not really what you're looking
>> for.  Or is it?
>>
>> CL-USER 21 > (defun f (n)
>>               (when (= n 0)
>>                 (break))
>>               (f (1- n)))
>> F
> 
> I was looking for something I could do from inside the function, not
> the REPL. In the example I gave, I can find out how deep the recursion
> is by looking at (length history), for example.

I don't believe that continuations are what you want, either.  It
provides one with a means to "capture the stack" but not the tools to
ask questions about it nor to access a binding at any frame in the
stack.  I think that CLtL2 environments <http://tinyurl.com/2u4h6v>
provide some level of introspection.  Duane Rettig gave a "mega-example"
of using the environment access interface to ask questions about
variable bindings.  I think he has also hinted at the possibility of an
implementation using environment access to implement the debugger but my
google-fu fails me at the moment.  But if you just want to count the 
number of calls to the function, for example, why not just count the 
number of calls to the function?

;; There is probably a more elegant way of doing this...
(defmacro defun-with-count (fn args &body forms)
   (with-gensyms (count)
     ` (defun ,fn ,args
         (let ((,count 0))
           (labels ((,fn ,args
                      (incf ,count)
                      (multiple-value-call #'values
                        (values ((lambda ,args ,@forms) ,@args))
                        ,count)))
             (,fn ,@args))))))

It still seems to me that the "how to capture information" depends on 
what kind of information and what you want to do with it.  Perhaps I'm 
missing something, though.
0
Reply dkixk (148) 7/10/2007 3:41:51 AM

In article <86abu5q7c3.fsf@deb.lib.aero.>, Ari Krupnik <ari@lib.aero> 
wrote:

> Barry Margolin <barmar@alum.mit.edu> writes:
> > Your hash-table approach requires you to explicitly copy the value of 
> > each variable into the hash table.  Instead, you could use a local 
> > function to access the variables you care about:
> >
> >    (flet ((get-local-state (var)
> >              (ecase var
> >                (var1 var1)
> >                (var2 var2)
> >                ...)))
> >      (f (cdr args) (cons #'get-local-state history)))
> 
> This looks very powerful, much more useful then the hash table I
> imagined. But I'm confused--shouldn't the keys be quoted? Otherwise,
> it seems to me the values of the local variables will become the keys,
> not their names:
>                 ('var1 var1)
>                 ('var2 var2)

CASE doesn't evaluate that part of the subform, so quoting is not 
required.  In fact, CASE allows you to put a list of objects there, e.g.

(ecase thing
  ((thing1 thing2) ...)
  ((thing3 thing4 thing5) ...)))

Since 'var1 is just shorthand for (quote var1), what you wrote would be 
equivalent to:

(ecase var
  ((quote var1) var1)
  ((quote var2) var2))

which would cause the value of VAR1 to be returned if VAR's value were 
the symbol QUOTE.

-- 
Barry Margolin, barmar@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
*** PLEASE don't copy me on replies, I'll read them in the group ***
0
Reply barmar (5664) 7/11/2007 4:36:48 AM

6 Replies
24 Views

(page loaded in 0.407 seconds)


Reply: