以两种方式展平列表:(i)使用MAPCAN和(ii)使用LOOP

时间:2010-09-09 17:57:12

标签: common-lisp

我的教授给了我们一个关于clisp的复习任务。一个练习是以三种方式实现相同的目的:返回给定列表中所有正整数的扁平列表。

现在,只有一种方式我真的喜欢这样做,使用cons和递归,但他希望我使用mapcan和循环来做这件事(我怀疑lisp不是他的首选语言因为这种编码风格对lisp的性质非常有抵抗力。我很难弄清楚如何使用循环来完成这个...我想首先要开始一个列表吗?

我为模糊的语言道歉,因为我不确定如何使用函数式语言来编写程序。以下是我的第一次尝试。

(defun posint-loop (l)  
  (loop for i in l
        do (if (listp i)  
               (posint-loop i)  
               (if (integerp i)  
                   (if (> i 0)  
                       (append i) ; this doesn't work because there's nothing to
                                  ; start appending to!
                       nil)  
                   nil))))

2 个答案:

答案 0 :(得分:2)

要建立新的词法绑定,请使用letwithloop关键字。为了扩展现有列表,您可能希望使用push;如果您需要原始订单,可以nreverse新列表finally

另一种方法是使用whencollectloop个关键字。

另一个提示:mapcan隐式创建一个新列表。

答案 1 :(得分:1)

Mapcan将一个函数应用于列表的每个元素,期望函数返回一个列表,然后将这些结果列表连接在一起。要将其应用于此问题,您只需处理顶级列表的每个元素。如果元素是列表,则需要递归处理它。如果不是,那么您需要返回一个空列表(不会在最终结果中添加任何元素)或仅包含该元素的列表(这将仅添加该元素到最终结果):

(defun flatten2 (list)
  (mapcan (lambda (x)
            (cond
             ((listp x) (flatten2 x))
             ((and (integerp x) (plusp x)) (list x))
             (t '())))
          list))

(flatten2 '((a 1 -4) (3 5 c) 42 0))
;=> (1 3 5 42)

使用循环,你可以做同样的事情,认识到(mapcan f list)在功能上等同于(列表nconc中的x循环(funcall fx))< / strong>即可。考虑到这一点,我们有:

(defun flatten3 (list)
  (loop for x in list
        nconc (cond 
               ((listp x) (flatten3 x))
               ((and (integerp x) (plusp x)) (list x))
               (t '()))))