Reputation: 2282
Here's a function I was writing that will generate a number list based on a start value, end value and a next function.
(defun gen-nlist (start end &optional (next #'(lambda (x) (+ x 1))))
(labels ((gen (val lst)
(if (> val end)
lst
(cons val (gen (next val) lst)))))
(gen start '())))
However when entering it into the SBCL repl I get the following warnings:
; in: DEFUN GEN-NLIST
; (SB-INT:NAMED-LAMBDA GEN-NLIST
; (START END &OPTIONAL (NEXT #'(LAMBDA (X) (+ X 1))))
; (BLOCK GEN-NLIST
; (LABELS ((GEN #
; #))
; (GEN START 'NIL))))
;
; caught STYLE-WARNING:
; The variable NEXT is defined but never used.
; (NEXT VAL)
;
; caught STYLE-WARNING:
; undefined function: NEXT
;
; compilation unit finished
; Undefined function:
; NEXT
; caught 2 STYLE-WARNING conditions
Somehow it does see the variable next as defined, but not used.
And where I do use it, as in (next val)
, it's an undefined function?!
Clearly I'm doing something wrong here. I just can't figure what or how. Is this the correct way of specifying optional function arguments with default values?
Upvotes: 5
Views: 2247
Reputation: 2282
In case if anyone ever does need to see the functioning code in full, thanks to the answer above and the comments, here's what I ended up with:
(defun gen-nlist (start end &optional (next #'1+) (endp #'>))
(labels ((gen (val)
(if (funcall endp val end)
'()
(cons val (gen (funcall next val))))))
(gen start)))
And here's the tail recursive version which doesn't suffer from a function call stack overflow, when attempting to create large lists:
(defun gen-nlist (start end &optional (next #'1+) (endp #'>))
(labels ((gen (val lst)
(if (funcall endp val end)
(reverse lst)
(gen (funcall next val) (cons val lst)))))
(gen start '())))
Usage examples:
> (gen-nlist 0 10)
(0 1 2 3 4 5 6 7 8 9 10)
> (gen-nlist 0 10 #'1+ #'equal)
(0 1 2 3 4 5 6 7 8 9)
> (gen-nlist 0 -10 #'1- #'<)
(0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10)
> (gen-nlist -10 0)
(-10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0)
Upvotes: 1
Reputation: 139251
The simple recursive version has a main problem: stack overflow for long lists. It's useful as a learning exercise, but not for production code.
The typical efficient loop iteration would look like this:
(defun gen-nlist (start end &optional (next #'1+) (endp #'>))
(loop for i = start then (funcall next i)
until (funcall endp i end)
collect i))
Upvotes: 8
Reputation: 6315
You have defined the optional argument the correct way, but as Common Lisp is a Lisp-2, it distinguishes between functions and variable values. The optional function is available as a variable, and has to be called using funcall
.
Replace
(cons val (gen (next val) lst))
with
(cons val (gen (funcall next val) lst))
and both the warning about the unused variable and the warning about the undefined function will disappear.
BTW: Your default function can be replaced with #'1+
Upvotes: 5