I used to write nested helper functions (which, btw, ocasionally used parameters of outer functions and are recursive) in Haskell like this (loop
):
sum a b = let
loop s i = if i > b then s else loop (s + i) (i + 1)
in loop 0 a
What is the clearest analog for this in Common Lisp?
I searched around here and found some discussions focused on returning functions from functions (and the problems which may arise when trying to call such “returned” functions) which is not quite the same situation, as far as I can see.
Labels is used to define local functions.
CL-USER> (defun thing (x)
(labels ((helper (y) (loop for i from x to y collect i)))
(helper 5)))
THING
CL-USER> (thing 1)
(1 2 3 4 5)
It is kind of like a let* statement for functions as you can define multiple local functions. So here we have helper and double-it being defined.
(defun thing (x)
(labels ((helper (y) (loop for i from x to y collect i))
(double-it (num) (* 2 num)))
(helper (double-it 10))))
Also you could use lambdas. Which in this instance is fairly tidy, though I still prefer labels for readability in this case.
CL-USER> (defun another-test (x)
(funcall #'(lambda (y) (loop for i from x to y collect i)) 10))
ANOTHER-TEST
CL-USER> (another-test 2)
(2 3 4 5 6 7 8 9 10)
Labels can also be used recursively:
CL-USER> (defun test-recurse (x)
(labels ((rec-list (count)
(when (< count x)
(cons count (rec-list (+ 1 count))))))
(rec-list 0)))
TEST-RECURSE
CL-USER> (TEST-RECURSE 10)
(0 1 2 3 4 5 6 7 8 9)
Hope it helps!
Just as a matter of playing "stupid Lisp tricks", I'd point out that in Scheme, the analog to
sum a b = let
loop s i = if i > b then sum else loop (s + i) (i + 1)
in loop 0 a
is letrec or a named let:
(define (sum a b)
(letrec ((loop (lambda (s i)
(if (> i b)
s
(loop (+ s i) (+ i 1))))))
(loop 0 a)))
(define (sum a b)
(let loop ((s 0) (i a))
(if (> i b)
s
(loop (+ s i) (+ i 1)))))
Letrec, because Scheme is a Lisp-1, gives you the functionality of labels
, which Baggers described. The named let can be done in Common Lisp with a macro around labels
:
(defmacro named-let (name bindings &body body)
`(labels ((,name ,(mapcar 'first bindings)
,@body))
(,name ,@(mapcar 'second bindings))))
(pprint (macroexpand
'(named-let loop ((s 0) (i a))
(if (> i b)
s
(loop (+ s i) (+ i 1))))))
;; (LABELS ((LOOP (S I)
;; (IF (> I B)
;; S
;; (LOOP (+ S I)
;; (+ I 1)))))
;; (LOOP 0 A))
However, tail calls aren't necessarily optimized away in Common Lisp, so this kind of recursion for iteration isn't all that common. The iterative analog is do
:
(defun sum (a b)
(do ((s 0 (+ s i))
(i a (+ i 1)))
((> i b) s)))
You can use loop
too, but it's a bit more verbose (but also probably more readable if you're familiar with do
:
(defun sum (a b)
(loop
for s = 0 then (+ s i)
for i = a then (+ i 1)
when (> i b) return s))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With