使用递归将原子列表转换为单个列表

时间:2013-08-07 13:44:47

标签: list recursion lisp scheme

我正在寻找一个能够递归地将原子列表转换为单个列表的答案。

一个例子是,(slist '(a (b c) (d e (f) g) h))进入(slist (a b c d e f g h))

任何答案都会有所帮助。

3 个答案:

答案 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?

您是否处理这种情况并收集树结构中存在的实际非列表原子,无论周期或不正确的终止?或者您是否准确地检测到情况并报告有意义的诊断?或者只是忽略这种可能性,让代码在较低级别的函数中爆炸,或执行失控的递归?

如果您不关心处理不正确的列表和循环结构,则会以递归方式定义列表的扁平化。

  1. 通过返回包含该原子的列表来展平非列表。
  2. 通过展平所有元素并将其连接来展平列表。
  3. 在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))))))