LISP 函数总是返回 nil
LISP function always returning nil
(defun insert (number lst)
(let ((before nil))
(if (= (length lst) 0) (return-from insert (list number)))
(loop for n from 0 to (1- (length lst)) do
(if (< number (nth n lst)) (progn (nconc before (list number)) (nconc before lst) (return-from insert before) )
(progn (nconc before (list (pop lst))))))
(nconc before (list number))
(return-from insert before)))
所以,我正在尝试插入一个在列表中排序的数字。请原谅我不太好的 LISP 练习,我才开始学习不久。
我正在浏览列表并将元素插入到 'before' 列表中。如果我要插入的数字小于我当前所在的列表元素,我将要插入的数字附加到 'before' 列表中,然后我将原始列表 'lst' 的剩余部分附加到 'before' 列表,然后返回 'before'.
但是,这个函数总是returns NIL。任何想法为什么?我的意思是,pop 和 nconc 都是破坏性的...
在 Common Lisp 中,从修改列表的意义上讲,在列表中插入 一个值并不是一个好习惯。相反,常见的做法是编写一个函数,给定一个列表,构建一个 new 列表,并将元素插入正确的位置。
例如,如果你想在一个已经排序的列表中插入一个数字,你可以用一个简单的递归函数来完成:
(defun insert (number lst)
(cond ((null lst) (list number))
((<= number (car lst)) (cons number lst))
(t (cons (car lst) (insert number (cdr lst))))))
(insert 3 '(1 2 5 9))
(1 2 3 5 9)
在您的函数中使用 nconc
因为它不仅应该修改列表,还应该修改包含它的变量。但如果变量是空列表,则不会发生这种情况。例如:
> (setq a nil)
NIL
> (nconc a (list 4 5))
(4 5)
> a
NIL
您应该始终将 nconc
的结果分配给您要修改的变量,例如 (setq a (nconc a (list 4 5)))
。
让我们看看你的代码的几个问题。
效率低下:错误的数据结构或错误的操作选择
如果您在列表中使用 LENGTH
或 NTH
,您可以打赌这是错误的。列表是链表,像 LENGTH
和 NTH
这样的操作可能很昂贵。特别是如果在循环或递归调用中完成了无数次。如果你想使用这些函数,那么 vectors 是自然的选择。如果您想使用列表,请尽量避免使用这些功能。链表唯一的 'cheap' 操作是链表头部的操作。
如果,RETURN
如果您需要 RETURN
或 RETURN-FROM
那么您很可能没有使用 Lisp 的内置数据流。
...
(if something (return ...))
...)
最好写成
...
(如果有什么
然后
否则)
循环
for n from 0 to (1- something)
只是
for n below something
NCONC
始终使用 NCONC
的 return 值。 return 值是串联列表。
内置功能
CL-USER 12 > (defun insert (number list)
(merge 'list list (list number) #'<))
INSERT
CL-USER 13 > (insert 10 (list 1 2 3 7 8 10 40))
(1 2 3 7 8 10 10 40)
(defun insert (number lst)
(let ((before nil))
(if (= (length lst) 0) (return-from insert (list number)))
(loop for n from 0 to (1- (length lst)) do
(if (< number (nth n lst)) (progn (nconc before (list number)) (nconc before lst) (return-from insert before) )
(progn (nconc before (list (pop lst))))))
(nconc before (list number))
(return-from insert before)))
所以,我正在尝试插入一个在列表中排序的数字。请原谅我不太好的 LISP 练习,我才开始学习不久。
我正在浏览列表并将元素插入到 'before' 列表中。如果我要插入的数字小于我当前所在的列表元素,我将要插入的数字附加到 'before' 列表中,然后我将原始列表 'lst' 的剩余部分附加到 'before' 列表,然后返回 'before'.
但是,这个函数总是returns NIL。任何想法为什么?我的意思是,pop 和 nconc 都是破坏性的...
在 Common Lisp 中,从修改列表的意义上讲,在列表中插入 一个值并不是一个好习惯。相反,常见的做法是编写一个函数,给定一个列表,构建一个 new 列表,并将元素插入正确的位置。
例如,如果你想在一个已经排序的列表中插入一个数字,你可以用一个简单的递归函数来完成:
(defun insert (number lst)
(cond ((null lst) (list number))
((<= number (car lst)) (cons number lst))
(t (cons (car lst) (insert number (cdr lst))))))
(insert 3 '(1 2 5 9))
(1 2 3 5 9)
在您的函数中使用 nconc
因为它不仅应该修改列表,还应该修改包含它的变量。但如果变量是空列表,则不会发生这种情况。例如:
> (setq a nil)
NIL
> (nconc a (list 4 5))
(4 5)
> a
NIL
您应该始终将 nconc
的结果分配给您要修改的变量,例如 (setq a (nconc a (list 4 5)))
。
让我们看看你的代码的几个问题。
效率低下:错误的数据结构或错误的操作选择
如果您在列表中使用 LENGTH
或 NTH
,您可以打赌这是错误的。列表是链表,像 LENGTH
和 NTH
这样的操作可能很昂贵。特别是如果在循环或递归调用中完成了无数次。如果你想使用这些函数,那么 vectors 是自然的选择。如果您想使用列表,请尽量避免使用这些功能。链表唯一的 'cheap' 操作是链表头部的操作。
如果,RETURN
如果您需要 RETURN
或 RETURN-FROM
那么您很可能没有使用 Lisp 的内置数据流。
...
(if something (return ...))
...)
最好写成
... (如果有什么 然后 否则)
循环
for n from 0 to (1- something)
只是
for n below something
NCONC
始终使用 NCONC
的 return 值。 return 值是串联列表。
内置功能
CL-USER 12 > (defun insert (number list)
(merge 'list list (list number) #'<))
INSERT
CL-USER 13 > (insert 10 (list 1 2 3 7 8 10 40))
(1 2 3 7 8 10 10 40)