Common Lisp中具有默认值的可选函数参数

时间:2016-09-12 10:51:58

标签: functional-programming common-lisp

这是我正在编写的一个函数,它将根据起始值,结束值和下一个函数生成一个数字列表。

(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)一样,它是一个未定义的函数?!

显然,我在这里做错了什么。我无法弄清楚是什么或如何。 这是使用默认值指定可选函数参数的正确方法吗?

3 个答案:

答案 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)