我认为您代码中的主要问题是在flatten 和butLast 的列表的cdr 上使用null? 或cdr。不要这样做;始终使用列表本身的过程和谓词。
我建议如下:
扁平化列表
大多数方案都有一个 flatten 内置版本,它负责嵌套列表和不正确的列表。你实现的版本不完全正确(试试(flatten '())),用这个:
(define (flatten lst)
(let loop ((lst lst) (res null))
(cond
((null? lst) res)
((pair? lst) (loop (car lst) (loop (cdr lst) res)))
(else (cons lst res)))))
> (flatten '(1 2 (3 (4 5 6))))
'(1 2 3 4 5 6)
> (flatten '(1 2 (3 (4 5 (6)))))
'(1 2 3 4 5 6)
> (flatten '())
'()
删除倒数第二个元素
所以现在这变得容易多了,循环遍历一个简单的平面正确列表,同时跟踪最后一个 (n-1) 和倒数第二个 (n-2) 元素。一个示例实现是:
(define (butSecondLastAtom lst)
(define flst (flatten lst))
(if (< (length flst) 2)
flst
(let loop ((flst (cddr flst)) (n-2 (car flst)) (n-1 (cadr flst)) (res null))
(if (null? flst)
(reverse (cons n-1 res)) ; here we drop the second-last element
(loop (cdr flst) n-1 (car flst) (cons n-2 res))))))
如果您想避免遍历列表两次(一次用于length,一次用于循环),您还可以自己跟踪长度:
(define (butSecondLastAtom lst)
(define flst (flatten lst))
(let loop ((lst flst) (len 0) (n-2 #f) (n-1 #f) (res null))
(if (null? lst)
(if (< len 2)
flst
(reverse (cons n-1 res))) ; here we drop the second-last element
(loop (cdr lst) (add1 len) n-1 (car lst) (if (< len 2) null (cons n-2 res))))))
测试
> (butSecondLastAtom '(1 2 (3 (4 5 6))))
'(1 2 3 4 6)
> (butSecondLastAtom '(1 2 (3 (4 5 (6)))))
'(1 2 3 4 6)
> (butSecondLastAtom '(((a))))
'(a)
> (butSecondLastAtom '())
'()