将Lisp代码形成任务 - 与flatten list方法相关

时间:2012-06-05 01:53:33

标签: lisp common-lisp

我在尝试为我想解决的问题构建代码时遇到问题。它是这样的:

〜目标:将嵌套列表展平为一个数字

  1. 如果对象是列表,请将列表替换为其原子总和。
  2. 使用嵌套列表,首先展平最里面的列表并从那里开始工作。
  3. 示例:

      (CONDENSE '(2 3 4 (3 1 1 1) (2 3 (1 2)) 5))
    
           (2 3 4 (6) (2 3 (3)) 5)
    
           (2 3 4 (6) (8) 5)
    
           (28) 
    
      => 28 
    

    我试图为这个问题实现展平列表功能,我最终得到了这个:

    (defun condense (lst)
      (cond
        ((null lst) nil)
        ((atom lst) (list lst)))
        (t (append  (flatten (apply #'+ (cdr lst))))))
    

    但它给了我错误:(

    有人可以向我解释我的处理/代码有什么问题吗?我怎样才能改进它?


    更新:2012年6月5日

    (defun condense(lxt)
      (typecase lxt
        (number (abs lxt))
        (list
            (if (all-atoms lxt)
               (calculate lxt)
               (condense (mapcar #'condense lxt))))))
    

    所以在这里,在这段代码中,显示了我的真实意图。我有一个函数calculate,它根据列表中的值执行计算。每次都不一定是相同的操作。另外,我知道我正在返回数字的绝对值;我这样做是因为我找不到另一种方法来返回数字本身。如果lxt是一个数字,我需要找到一种方法来返回数字。我让它在底部递归两次,因为这是一种无限循环的方式,直到它计算出一个数字。注意:此功能不再实现展平功能,也不使用任何内容。

5 个答案:

答案 0 :(得分:2)

这是家庭作业吗?如果是这样,请将其标记为。一些提示:

  1. 您确定nil中空列表的“缩写”吗? (也许你应该给一个号码?)
  2. 你确定一个元素的condensation是一个列表吗? (也许你应该给一个号码?)
  3. 你确定最后一个案例的condensation是一个列表吗? (你不应该给一个号码回复)?
  4. 简而言之,如果您的所有返回值都是列表,那么condense将如何返回28

答案 1 :(得分:2)

想象一下你已经拥有了自己的功能。它得到了什么?它必须产生什么?

给定一个原子,它会返回什么?给出一个简单的原子列表,它应该返回什么?

(defun condense (x)
  (typecase x
    (number  
       ; then what?
       (condense-number x))
    (list
       ; then what?
       (if (all-atoms x)
         (condense-list-of-atoms x) ; how to do that?
         (process-further-somehow
            (condense-lists-inside x))))
    ; what other clauses, if any, must be here?
    ))

condense-lists-inside必须做什么?根据你的描述,它是凝聚内部的嵌套列表 - 每个列入一个数字,并保持原子完好无损。所以它会留下一个数字列表。为了进程 进一步以某种方式,我们已经“拥有”一个函数condense-list-of-atoms,对吗?

现在,如何实施condense-lists-inside?这很容易,

(defun condense-lists-inside (xs)
  (mapcar #'dowhat xs))

为什么,condense,当然!记住,我们想象我们已经拥有它。只要它达到了它的意义,就应该生产出它的设计产品。也就是说,给定一个原子或一个列表(里面可能有嵌套列表),它将生成一个数字

所以现在,填写空白,并简化。特别是,看看你是否真的需要all-atoms检查。

编辑 实际上,使用typecase是一个不幸的选择,因为它将NIL视为LIST。我们需要以不同的方式处理NIL,而是返回“零值”。因此,最好使用通常的(cond ((null x) ...) ((numberp x) ...) ((listp x) ...) ... )构造。

关于你的新代码:你错了:要处理(mapcar #'condense x)之后返回的原子列表,我们有一个函数calculate来做到这一点,不需要走得那么远{ {1}}本身。当您在那里替换condense时,很明显根本不需要检查calculate;它只是一个教学设备,以简化代码的开发。 :)当我们开发时可以做出多余的选择,如果我们然后将它们简化, 之后我们已经实现了 正确性的目标< / EM>

但是,删除all-atoms支票会破坏您的要求#2。然后计算将如下进行

all-atoms

即。它将以从左到右的方式进行,而不是从最深层次的方式进行。将嵌套列表想象成一棵树(就是这样),这将从树的最深处向上和向右“咀嚼”树; (CONDENSE '(2 3 4 (3 1 1 1) (2 3 (1 2)) 5)) == (calculate (mapcar #'condense '(2 3 4 (3 1 1 1) (2 3 (1 2)) 5))) == (calculate (list 2 3 4 (condense '(3 1 1 1)) (condense '(2 3 (1 2))) 5)) == (calculate (list 2 3 4 (calculate '(3 1 1 1)) (calculate (list 2 3 (calculate '(1 2)))) 5)) == (calculate (list 2 3 4 6 (calculate '(2 3 3)) 5)) == (calculate (list 2 3 4 6 8 5)) == 28 检查的代码将严格按级别进行。

所以最终的简化代码是:

all-atoms

一条评论 看一下缩减序列的最后一个插图,出现了一个清晰的图片 - 替换参数中的每个节点 tree ,带有计算应用程序。这是folding的一个明显的例子,就像(defun condense (x) (if (listp x) (reduce #'+ (mapcar #'condense x)) (abs x))) 那样在树上而不是普通列表上完成。

这可以直接使用所谓的“car-cdr递归”进行编码,将两个递归调用reduce的两个结果替换为每个cons单元,并应用组合函数f。 }和car单元格的组件:

cdr

正如您所看到的,这个版本是高度递归的,这不是那么好。

答案 2 :(得分:0)

任务:使用嵌套列表,首先展平最里面的列表并从那里开始工作

sum
   flatten lists

总和使用REDUCE,而不是APPLY

对于展平列表,您需要一个循环。 Lisp已经提供了专门的映射函数。

略高一点:总和和展平都可以通过拨打REDUCE来完成。

你也可以在不使用像APPLY,REDUCE等更高阶函数的情况下写下递归。这样做的工作量更多。

答案 3 :(得分:0)

这里添加了您所遇到的错误的解释,实际上您已经接近解决问题,只需要付出更多的努力就可以了。

; compiling (DEFUN CONDENSE ...)

; file: /tmp/file8dCll3
; in: DEFUN CONDENSE
;     (T (APPEND (FLATTEN (APPLY #'+ (CDR LST)))))
; 
; caught WARNING:
;   The function T is undefined, and its name is reserved 
;   by ANSI CL so that even
;   if it were defined later, the code doing so would not be portable.
; 
; compilation unit finished
;   Undefined function:
;     T
;   caught 1 WARNING condition
;STYLE-WARNING: redefining CONDENSE in DEFUN

(defun condense (lst)
  (cond
    ((null lst) nil)
    ((atom lst) (list lst)))
  ;.------- this is a function call, not a condition
  ;|         (you closed the parens too early)
  (t (append  (flatten (apply #'+ (cdr lst))))))

;; Argument Y is not a NUMBER: (3 1 1 1)
;;    [Condition of type SIMPLE-TYPE-ERROR]

(defun condense (lst)
  (cond
    ((null lst) nil)
    ((atom lst) (list lst));             .-- not a number!
    ;You are calling #'+ -------.        |
    ;on something, which        |  '(3 4 (3 1 1 1) (2 3 (1 2)) 5)
    ; is not a number.          |   |
    (t (append  (flatten (apply #'+ (cdr lst)))))))

;; You probably wanted to flatten first, and then sum

(defun condense (lst)
  (cond
    ((null lst) nil);          .--- returns just the 
    ((atom lst) (list lst));  /     atom 28, you can
    ;  .---------------------/      just remove it.
    (t (append (apply #'+ (flatten lst))))))

;; Now, you are lucky that append would just return the 
;; atom if it's not a list

(defun condense (lst)
  (cond
    ((null lst) nil)
    ((atom lst) (list lst))
    (t (apply #'+ (flatten lst)))))

;; Again, you are lucky because (apply can take enough arguments
;; while your list is reasonably small - this will not always be
;; the case, that is why you need to use something more durable,
;; for example, reduce.

(defun condense (lst)
  (cond
    ((null lst) nil)
    ((atom lst) (list lst))
    (t (reduce #'+ (flatten lst)))))

;; Whoa!

(condense '(2 3 4 (3 1 1 1) (2 3 (1 2)) 5))

这是flatten函数实际工作的全部内容。

答案 4 :(得分:0)

如果您的lisp已经实现了flattenreduce函数(例如我将在这里使用的Clojure),您可以执行以下操作:

user=> (defn condense [l] (reduce + 0 (flatten l)))
#'user/condense
user=> (condense [1 [2 [[3 4] 5]]])
15
user=> 

如果做不到,那些功能的天真实现可能是:

(defn flatten [l]
  (cond (nil?  l) l
        (coll? l) (let [[h & t] l]
                    (concat (flatten h) (flatten t)))
        true      [l]))

(defn reduce [op initial-value [h & t]]
  (if (nil? t)
    (op initial-value h)
    (op initial-value (reduce op h t))))

但请务必检查您正在使用的特定Lisp的语义。此外,如果您正在实现reduceflatten,您可能希望将它们设置为尾递归,而我并不是为了保持清晰。

在Common Lisp中你会做类似的事情:

(defun flatten (l)
    (cond ((null l) l)
          ((atom l) (list l))
          (t        (append (flatten (car l))
                            (flatten (cdr l))))))

并使用apply而不是reduce:

(defun condense (l) (apply #'+ (flatten l)))