views:

89

answers:

3

I've been writing more Lisp code recently. In particular, recursive functions that take some data, and build a resulting data structure. Sometimes it seems I need to pass two or three pieces of information to the next invocation of the function, in addition to the user supplied data. Lets call these accumulators.

What is the best way to organize these interfaces to my code?

Currently, I do something like this:

(defun foo (user1 user2 &optional acc1 acc2 acc3)
    ;; do something
    (foo user1 user2 (cons x acc1) (cons y acc2) (cons z acc3)))

This works as I'd like it to, but I'm concerned because I don't really need to present the &optional parameters to the programmer.

3 approaches I'm somewhat considering:

  • have a wrapper function that a user is encouraged to use that immediately invokes the extended definiton.

  • use labels internally within a function whose signature is concise.

  • just start using a loop and variables. However, I'd prefer not since I'd like to really wrap my head around recursion.

Thanks guys!

+2  A: 

Your impulse to shield implementation details from the user is a smart one, I think. I don't know common lisp, but in Scheme you do it by defining your helper function in the public function's lexical scope.

(define (fibonacci n)
  (let fib-accum ((a 0)
                  (b 1)
                  (n n))
    (if (< n 1)
        a
        (fib-accum b (+ a b) (- n 1)))))

The let expression defines a function and binds it to a name that's only visible within the let, then invokes the function.

Jonathan Feinberg
Yes, I think Common Lisp's `labels` is pretty much equivalent to Scheme's `let` in this case.
Ken
+4  A: 

If you want to write idiomatic Common Lisp, I'd recommend the loop and variables for iteration. Recursion is cool, but it's only one tool of many for the Common Lisper. Besides, tail-call elimination is not guaranteed by the Common Lisp spec.

That said, I'd recommend the labels approach if you have a structure, a tree for example, that is unavoidably recursive and you can't get tail calls anyway. Optional arguments let your implementation details leak out to the caller.

Nathan Sanders
Thanks. I had some functions dealing with trees, so I converted them to use `labels`. Another set of functions operating just on lists, so I used that opportunity to get familiar with `loop`.
Willi Ballenthin
A: 

I have used all the options you mention. All have their merits, so it boils down to personal preference.

I have arrived at using whatever I deem appropriate. If I think that leaving the &optional accumulators in the API might make sense for the user, I leave it in. For example, in a reduce-like function, the accumulator can be used by the user for providing a starting value. Otherwise, I'll often rewrite it as a loop, do, or iter (from the iterate library) form, if it makes sense to perceive it as such. Sometimes, the labels helper is also used.

Svante