这是我正在编写的一个函数,它将根据起始值,结束值和下一个函数生成一个数字列表。
(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 '())))
然而,当将其输入SBCL repl时,我收到以下警告:
; 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
不知何故,它确实看到了变量next,但未使用。
我在哪里使用它,就像(next val)
一样,它是一个未定义的函数?!
显然,我在这里做错了什么。我无法弄清楚是什么或如何。 这是使用默认值指定可选函数参数的正确方法吗?
答案 0 :(得分:8)
简单的递归版本有一个主要问题:长列表的堆栈溢出。它可用作学习练习,但不适用于生产代码。
典型的有效循环迭代如下所示:
(defun gen-nlist (start end &optional (next #'1+) (endp #'>))
(loop for i = start then (funcall next i)
until (funcall endp i end)
collect i))
答案 1 :(得分:5)
您已经以正确的方式定义了可选参数,但由于Common Lisp是一个Lisp-2,它区分了函数和变量值。可选功能可用作变量,必须使用funcall
调用。
替换
(cons val (gen (next val) lst))
与
(cons val (gen (funcall next val) lst))
并且关于未使用变量的警告和关于未定义函数的警告都将消失。
BTW:您的默认功能可以替换为#'1+
答案 2 :(得分:0)
如果有人确实需要完整地查看功能代码,感谢上面的答案和评论,这就是我最终的结果:
(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)))
这是尾递归版本,在尝试创建大型列表时不会受到函数调用堆栈溢出的影响:
(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 '())))
用法示例:
> (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)
&GT; (gen-nlist 0 -10 #'1- #'<)
(0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10)
&GT; (gen-nlist -10 0)
( - 10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0)