我正在尝试使用GNU ClISP在Common Lisp中编写一个程序来编译它。我想输入一个列表,例如(A(B (C) ()) (D (E) (F (G) ())))
,并根据打印出前,中,或后顺序遍历的第一个单词。示例:
(pre '(A(B (C)... etc))
我无法将逻辑输入Clisp表示法。我目前有以下代码:
(defun leftchild (L)(cadr L))
(defun rightchild (L)(caddr L))
(defun data (L)(car L))
(defun pre (L)(if (null L) '()((data L)(pre(leftchild L))(pre(rightchild L)))))
... similar in and post functions
我得到编译错误,说我应该在我的pre函数中使用lambda。我认为这是由于双重((数据前面因为它期待一个命令,但我不确定我应该放在那里。我不认为cond会起作用,因为这会妨碍递归循环。此外,数据L会像现在一样打印吗?编译器无法识别(print (data L))
。
我已经在这个代码上工作了一个多星期,试图自己排除故障,但我不知所措。如果有人能够解释我做错了什么,我将不胜感激。
我的另一个问题是如何让程序提示用户输入一行(pre'(A ... etc)),这样当我运行编译的文件时,程序将运行而不是给出一个funcall错误?
感谢您的时间。
答案 0 :(得分:1)
简短回答:如果您想使用if
,请注意您需要progn
才能在后续和替代案例中包含多个表单。
长答案 - 还解释了如何遍历在列表中累积受访节点:
我想这是作业,所以我不会给你一个完整的解决方案,但你的问题表明你基本上有正确的想法,所以我会告诉你一个简单,惯用的方法来做到这一点。
首先,你是对的:一个不带引号的形式的汽车应该是一个函数,所以基本上像(foo ...)
,foo
不是函数(或宏,特殊形式......) ),整个事情是要评估,将是一个错误。请注意,这不适用于特殊形式和宏(例如cond
)。这些可以更改评估规则,而不是看起来像(foo bar)
的所有内容都必须是要由正常评估规则评估的表单。最简单的示例是quote
,它只会返回其参数未评估,因此(quote (foo bar))
不会出错。
现在,关于你的问题:
一个简单的解决方案是使用累加器和遍历树的递归辅助函数,并将值推送到累加器中。像这样:
(defun pre (node)
(let ((result (list)))
(labels ((rec (node)
(cond (...
...
...))))
(rec node)
(nreverse result))))
labels
只引入了一个本地帮助函数,它将执行实际的递归,而外let
为您提供了一个累加器来收集节点值。此解决方案将结果作为列表返回。如果您只想打印每个节点值,则不需要累加器或辅助函数。只需打印而不是推动,并使助手成为你的顶级功能。
请记住,您需要一个递归停止的基本案例。您应该在cond
中查看该内容。然后,您将需要每个子树的递归步骤,并且您需要将节点的值推送到结果。您执行这些步骤的顺序决定了您是在进行预订,中间或后订单遍历。您的代码表明您已经理解了这个原则,因此您只需要使用Lisp代码即可。您可以使用push
将值推送到result
和consp
以检查节点是否为非空列表。由于空列表无关,因此基本上只需要在cond
中进行一次测试,但您也可以像在代码中一样明确检查节点是否为null
。