Common lisp 公共Lisp二叉树

Common lisp 公共Lisp二叉树,common-lisp,binary-tree,clisp,Common Lisp,Binary Tree,Clisp,我正在尝试用通用Lisp编写一个程序,使用GNU ClISP来编译它。我想输入一个列表,例如(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

我正在尝试用通用Lisp编写一个程序,使用GNU ClISP来编译它。我想输入一个列表,例如
(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。我认为这是由于double((在数据前面,因为它需要一个命令,但我不确定应该放在那里。我认为cond不起作用,因为这会阻碍递归循环。另外,数据L是否会像现在一样打印?编译器没有识别
(打印(数据L))

我已经在这段代码上工作了一个多星期,试图自己解决它,但我不知所措。如果有人能解释一下我做错了什么,我将不胜感激

我的另一个问题是如何让程序提示用户输入(pre'(a…etc)),这样当我运行编译后的文件时,程序将运行,而不是给出funcall错误


谢谢您的时间。

简短回答:如果您想使用
If
,请注意,您需要一个
progn
,以便在后续和备选情况下有多个表单


长答案–还解释了如何遍历列表中的已访问节点:

我想这是家庭作业,所以我不会给你一个完整的答案,但你的问题表明你基本上有正确的想法,所以我将向你展示一种简单、惯用的方法

首先,你是对的:一个不带引号的表单的car应该是一个函数,所以基本上类似于
(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
中检查这一点。然后,您需要为每个子树执行递归步骤,并且需要将节点的值推送到结果中。执行这些步骤的顺序决定了您是在进行顺序前遍历、顺序中遍历还是顺序后遍历。您的代码ws您已经理解了这个原理,所以您只需要在Lisp代码中使用它。您可以使用
push
将值推送到
result
,并使用
consp
检查节点是否为非空列表。由于对空列表没有任何操作,您基本上只需要在
cond
中进行一次测试,但是您还可以明确检查节点是否为
null
,就像您在代码中所做的那样。

对不起,我略读了您的问题有点太快了。:)您的问题只是将
if
不带
progn
用于多种形式(请参见我答案的最后一句)。即,您的
pre
应该如下所示:
(defun pre(n)(如果(null n)“”()(progn(print(data n))(pre(leftchild n))(pre(rightchild n‘‘‘‘)’)
。在这种情况下,您也可以在
consp
同时使用
时使用
,而不使用
progn
。我将保留原样的答案,因为我认为它对搜索“[lisp]二叉树”的人很有用.Btw.,为什么您认为
cond
“会阻碍递归循环”?感谢您的回答。我认为cond可能会阻碍循环,因为它需要您将所有条件设置为true,程序才能运行所有“else”案例,但现在我认为它可以工作了,因为初始的if语句检查null。我添加了您建议的progn,它遍历循环并像应该的那样打印出来。如何防止它在列表末尾打印nil?我修复了它。它不再打印nil。我现在遇到的唯一问题是尝试打印到一个文件,但我会继续尝试。:)关于
cond
:如果您有多个测试,这很好,而且对于一个分支中的多个表单,它不需要
progn
。您可以使用
if
执行的所有操作,也可以使用
cond
执行。您甚至可以使用它编写自己的
if
(defmy-if(条件后续替代)`(条件(,条件,后续)(t,替代))
(未测试)。要超过
nil
(这只是返回值,如果您在REPL中不使用代码,它实际上不会打印),您可以插入
(值)
,作为
程序中的最后一个表单。