【发布时间】:2013-08-07 13:44:47
【问题描述】:
我正在寻找将原子列表递归地转换为单个列表的答案。
例如,(slist '(a (b c) (d e (f) g) h)) 变为 (slist (a b c d e f g h))
任何答案都会有所帮助。
【问题讨论】:
标签: list recursion lisp scheme
我正在寻找将原子列表递归地转换为单个列表的答案。
例如,(slist '(a (b c) (d e (f) g) h)) 变为 (slist (a b c d e f g h))
任何答案都会有所帮助。
【问题讨论】:
标签: list recursion lisp scheme
您正在尝试做的事情称为扁平化列表。这里有很多选项:
; 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)
根据您使用的解释器,它很可能已经包含一个实现。例如,在球拍中:
(flatten '(a (b c) (d e (f) g) h))
=> '(a b c d e f g h)
【讨论】:
在 Lisp 中,与 Scheme 不同,您必须接受这样一个事实,即原子 nil 表示列表结构中的一个空列表。所以,严格来说,当你展平一个列表时,你并没有从树结构中获得所有的原子;只有那些不代表空列表和列表终止符的。
您还必须做出设计决定:您的代码是否处理不正确的列表和循环列表?也就是说,这些case应该怎么办:
(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 具有破坏性,但这在这里并不存在问题,因为它只会连接在我们的函数调用中构造的列表结构,而不是任何传入的列表结构。换句话说,我们的输出与输入不共享结构。
【讨论】:
你已经检查了一个正确的答案,但这里有一个简单的实现,它清楚地表明了递归:
(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))))))
【讨论】: