我在尝试为我想解决的问题构建代码时遇到问题。它是这样的:
〜目标:将嵌套列表展平为一个数字
示例:
(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
是一个数字,我需要找到一种方法来返回数字。我让它在底部递归两次,因为这是一种无限循环的方式,直到它计算出一个数字。注意:此功能不再实现展平功能,也不使用任何内容。
答案 0 :(得分:2)
这是家庭作业吗?如果是这样,请将其标记为。一些提示:
nil
中空列表的“缩写”吗? (也许你应该给一个号码?)condensation
是一个列表吗? (也许你应该给一个号码?)condensation
是一个列表吗? (你不应该给一个号码回复)? 简而言之,如果您的所有返回值都是列表,那么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已经实现了flatten
和reduce
函数(例如我将在这里使用的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的语义。此外,如果您正在实现reduce
和flatten
,您可能希望将它们设置为尾递归,而我并不是为了保持清晰。
在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)))