sjb
sjb

Reputation: 55

recursive function return using block not working

[solved]

I have something similar with these four functions: base, init, func and some. The func is recursive and calls itself: in the "stop case" it would call some and return its value, then it should return control back to "init", wherefrom it is invoked; the latter being once called from base.

base
  -> init
       -> func
            -> init
                 -> func
                      -> some
                    |
           _________+
          |
          v
          ; should continue from here (in `func`)

[not anymore]

Instead, after the first call to some, the control is yielded directly to base, skipping what I would expect to be the intermediate (init,func) pair call(s).

I actually tried several simpler cases using block, return and recursion (e.g., "mutual tail-recursive factorial"), and all worked well. I mention that func uses a test helper function that catch a throw (but I tried even an example with (catch 'test (throw 'test 0)), and it was ok); just so whatever could my real program have something causing the issue.

This is elisp: each defun commences with block, and all functions use return, as in the following.

[I switched from using "defun/block" to "defun*"]

(defmacro 4+ (number)
  "Add 4 to NUMBER, where NUMBER is a number."
  (list 'setq number (list '1+ (list '1+ (list '1+ (list '1+ number))))))

(defmacro 4- (number)
  "Subtract 4 from NUMBER, where NUMBER is a number."
  (list 'setq number (list '1- (list '1- (list '1- (list '1- number))))))

(defun mesg (s &optional o)
  "Use ATAB to tabulate message S at 4-multiple column; next/prev tab if O=1/0."
  (when (null o) (setq o 0))
  (case o (0 (4- atab)) (1 nil))
  (message (concat "%" (format "%d" (+ atab (length s))) "s") s)
  (case o (0 nil) (1 (4+ atab))))

(defun* base ()
  (let (pack)
    (setq atab 0)
    (mesg "base->" 1)
    (setq pack (init))
    (mesg "<-base")))

(defun* init ()
  (mesg "init->" 1)
  (return-from init (progn (setq temp (func)) (mesg "<-init") temp)))

(defun* func (&optional pack)
  (mesg "func->" 1)
  (when (not (null pack)) (return-from func (progn (mesg "<+func") pack)))
  (when (< 0 (mod (random) 2)); stop case
    (return-from func (progn (setq temp (some)) (mesg "<-func") temp)))
  (setq pack (init))
  (case (mod (random) 2)
    (0 (return-from func (progn (mesg "<0func") pack)))
    (1 (return-from func (progn (setq temp (func pack)) (mesg "<1func") temp))) ; use tail-recursion instead of `while'
    (t (error "foo bar"))))

(defun* some ()
  (mesg "some->" 1)
  (return-from some (progn (mesg "<-some") (list 2 3 4))))

(base)

The pack variable is my value-list as data structure. I also use func to reiterate itself (in tail-recursive call) with a special accumulating-parameter so that I avoid "imperative" while.

So instead of what I would expect (each > is paired by <)

base->
    init->
        func->
            init->
                func->
                    some->
                    <-some
                <-func
            <-init
            func-> ; tail-recursion
            <+func
        <1func
    <-init
<-base

my program behaves as follows.

base
  -> init
       -> func
            -> init
                 -> func
                      -> some
                           |
 __________________________+
|
v
; control yielded here (to `base`)

[not anymore]

Why is the control yielded too soon back to the start of the program, and not continue in the first call to func, after return from the second call via init?

Appreciate any help,

Sebastian

Upvotes: 2

Views: 152

Answers (2)

sjb
sjb

Reputation: 55

Thanks both for your answer: inserting into my program those messages I tried as with the code I added for explanations revealed there are no defun* problems with elisp, but some things I mistook in design.

Upvotes: 0

Diego Sevilla
Diego Sevilla

Reputation: 29011

Looking at your code, it is not clear to me what's the extent of the block in func. If the block includes the whole func definition, then yes, the control reaches func when returning, but the block is skipped completely, hence the function completely, and comes back all the way up where it was called (eventually base). May be that the case?

If that's so, you have to put the code that you want to execute after a return after the block.

EDIT: Looking again at your code, I think you're not using the return as it should be used. For instance in init you have

(block nil
 ...

 (return (func ...)))

This return "cancels" the block, and takes the same effect as not having the block at all, unless some function called in "..." does have a return without a block. So the return here cancels the possible return points of func.

Upvotes: 1

Related Questions