在不使用 nthcdr 的情况下获取列表的第 n 个 CDR



我是一个普通的Lisp初学者,正在为我的一个作业而苦苦挣扎......

我的CS任务之一是创建一个函数,该函数基本上用作clisp的内置nthcdr函数。

我们称之为ncdr:

(ncdr n L( => n 是我们想要输出的 CDR,L 是列表

例子:

(ncdr 2 '(a b c)) => (c)
(ncdr 0 '(a b c)) => (a b c)
(ncdr -1 '(a b c)) => nil
(ncdr 5 '(a b c)) => nil

所以我的方法是设置一个计数器并将其与 n 进行比较

以下是我想到的条件:

if n < 0 -> nil 
if counter < n -> + 1 counter and (ncdr (cdr list)
if counter = n -> output list
if counter > n -> nil 

这就是我想出的...我不认为这是最有效的方法,就目前而言,它不起作用。

(defun ncdr (n L &aux counter)
(setq counter 0)
(cond
((atom L) nil)
((< n 0) nil)
((< counter n) (+ 1 counter (ncdr n (cdr L))))
((eq counter n) L)
((> counter n) nil) ))

从我花在试验函数的每个 cond 行上的时间来看,我知道这条线无法按预期工作

((< counter n) (+ 1 counter (ncdr n (cdr L))))

我收到一个错误:nil 不是数字

我觉得我错过了一些解决这个问题的关键东西。

首先,错误。在:

(+ 1 counter (ncdr n (cdr L))))

你实际上是在对三个值求和:1,变量counter的值,以及函数的结果ncdr应用于两个参数,当nil时,会产生错误(例如,你正在求和(+ 1 0 nil)nil不是一个数字(。

然后,让我们转到程序的逻辑。

您的条件基本上是正确的,尽管它们导致了一个可以改进的程序,正如我们稍后将看到的那样。

但是,你在它们的实现中犯了一个逻辑错误:counter应该在函数的每次递归调用时更改,增加 1,而对于每次调用,在函数主体执行开始时将其重置为零。这是因为即使正确完成,增量也会被分配(setq counter 0)取消。

我们如何正确增加这样的变量?通过在函数中添加一个新参数counter,并避免每次都将其重置为零,而仅在初始调用开始时将其分配给零。

如何在 Common Lisp 中做到这一点?通过以下方式使用可选参数,而不是辅助变量:

(defun ncdr (n L &optional (counter 0))
(cond ((atom L) nil)
((< n 0) nil)
((< counter n) (+ 1 counter (ncdr n (cdr L))))  ; <- Wrong!!! To be corrected
((eq counter n) L)
((> counter n) nil)))

因此,让我们修改条件的递归分支,以在递归调用中正确传递新值(并执行一些小的调整(:

(defun ncdr (n L &optional (counter 0))
(cond ((atom L) nil)
((< n 0) nil)
((< counter n) (ncdr n (cdr L) (+ 1 counter)))  ; or (1+ counter)
((= counter n) L)                               ; use = for numbers
((> counter n) nil)))

lambda 列表中的&optional关键字具有以下效果:最初您调用(ncdr 2 '(a b c))因此counter被分配给 0,而在其余调用中,当前值被传递,递增 1,以便在递归结束时它将等于n

效率

无需使用另一个变量counter,因为您已经有一个适合递归的变量:n。事实上,你可以这样重新制定你的条件:

if the list is empty, return nil
if n is less then 0, return nil
if n is 0, return the list
if n is greater than 0, recur on n-1 and on the cdr of the list

引入具有以下结构的程序:

(defun ncdr (n L)
(cond ((null L) nil)
((< n 0) nil)
((= n 0) L)
(t (ncdr ...))  ; <- recursive call - to be completed as exercise!

for VisualLisp (AutoCAD(:

(defun ncdr (n L)
(if (< n 0)
(setq L L)
(repeat (+ n 1) (setq L (cdr L)))
)
)

这是一个循环,将列表 L 的第一个元素切割 n+1 次。从 0 开始。

例:

(setq L (list 0 1 2 3 4)); -->(0 1 2 3 4)
(ncdr 0 L); -->    (1 2 3 4)
(ncdr 1 L); -->    (2 3 4)
(ncdr 2 L); -->    (3 4)
(ncdr 3 L); -->    (4)
(ncdr 4 L); -->    nil

相关内容

  • 没有找到相关文章

最新更新