我正在寻找一个能够递归地将原子列表转换为单个列表的答案。
一个例子是,(slist '(a (b c) (d e (f) g) h))
进入(slist (a b c d e f g h))
任何答案都会有所帮助。
答案 0 :(得分:3)
您尝试做的事情称为展平列表。以下是一系列选项:
; required predicate
(define (atom? x)
(and (not (null? x))
(not (pair? x))))
; naïve version using append
(define (flatten1 lst)
(cond ((null? lst)
'())
((not (pair? lst))
(list lst))
(else
(append (flatten1 (car lst))
(flatten1 (cdr lst))))))
; naïve version using append, map, apply
(define (flatten2 lst)
(if (atom? lst)
(list lst)
(apply append (map flatten2 lst))))
; efficient version using fold-left
(define (flatten3 lst)
(define (loop lst acc)
(if (atom? lst)
(cons lst acc)
(foldl loop acc lst)))
(reverse (loop lst '())))
; very efficient version with no higher-order procedures
(define (flatten4 lst)
(let loop ((lst lst)
(acc '()))
(cond ((null? lst)
acc)
((not (pair? lst))
(cons lst acc))
(else
(loop (car lst) (loop (cdr lst) acc))))))
以上任何一种都可以按预期工作。例如,使用flatten4
:
(flatten4 '(a (b c) (d e (f) g) h))
=> '(a b c d e f g h)
根据您使用的口译员,很可能已经包含了一个实施方案。例如,在Racket中:
(flatten '(a (b c) (d e (f) g) h))
=> '(a b c d e f g h)
答案 1 :(得分:1)
在Lisp中,与Scheme相反,您必须接受原子nil
表示列表结构中的空列表这一事实。因此,严格来说,当您展平列表时,您不会从树结构中获得所有原子;只有那些不代表空列表和列表终结符的那些。
您还必须做出设计决定:您的代码是否处理不正确的列表和循环列表?也就是说,这些案例应该做什么:
(flatten '(a . b)) ;; (a b), accurate diagnostic or failure?
(flatten '#1=(a . #1#)) ;; (a), accurate diagnostic or failure?
您是否处理这种情况并收集树结构中存在的实际非列表原子,无论周期或不正确的终止?或者您是否准确地检测到情况并报告有意义的诊断?或者只是忽略这种可能性,让代码在较低级别的函数中爆炸,或执行失控的递归?
如果您不关心处理不正确的列表和循环结构,则会以递归方式定义列表的扁平化。
在Lisp中,编写代码通常比英文规范或伪代码更简单,更清晰:
(defun flatten (obj)
"Simple flatten: no handling of improper lists or cycles"
(if (listp obj)
(mapcan #'flatten obj)
(list obj)))
请注意,尽管mapcan
具有破坏性,但这并不存在问题,因为它只会链接在函数调用中构造的列表结构,而不是任何传入的列表结构。换句话说,我们的输出不与输入共享结构。
答案 2 :(得分:0)
您已经检查了正确答案,但这里有一个简单的实现,可以清楚地表明递归:
(define (slist list)
(if (null? list)
'()
(let ((next (car list))
(rest (cdr list)))
(if (list? next)
(append (slist next) (slist rest))
(cons next (slist rest))))))