Common Lisp 二叉树

发布于 2024-12-08 17:33:24 字数 776 浏览 0 评论 0原文

我正在尝试使用 GNU ClISP 在 Common Lisp 中编写一个程序来编译它。我想输入一个列表,例如 (A(B (C) ()) (D (E) (F (G) ()))) 并根据第一个单词打印出前序、中序或后序遍历。示例:

(pre '(A(B (C)... etc))

我无法将我的逻辑转化为 Clip 表示法。我目前有以下代码:

(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 不起作用,因为这会阻碍递归循环。另外, data L 会按现在的样子打印吗?编译器无法识别(print (data L))

我已经研究这个代码一个多星期了,试图自己排除故障,但我如果有人我会很感激。可以解释我做错了什么。

我的另一个问题是如何让程序提示用户输入 (pre '(A... etc)) ,以便当我运行编译的文件时该程序会运行而不是给出 funcall 错误吗?

谢谢您的时间。

I am trying to write a program in Common Lisp using GNU ClISP to compile it. I would like to enter a list such as (A(B (C) ()) (D (E) (F (G) ()))) and depending on the first word print out the pre-, in-, or post-order traversal. Example:

(pre '(A(B (C)... etc))

I am having trouble putting my logic into Clisp notation. I currently have the following code:

(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

I get compiling errors saying that I should use a lambda in my pre function. I think this is due to the double (( infront of data because it is expecting a command, but I am not sure what I should put there. I don't think cond would work, because that would hinder the recursive loop. Also, will data L print as it is now? The compiler did not recognize (print (data L)).

I have been working on this code for over a week now, trying to troubleshoot it myself, but I am at a loss. I would greatly appreciate it if someone could explain what I am doing incorrectly.

Another question that I have is how can I make the program prompt a line to the user to enter the (pre '(A... etc)) so that when I run the compiled file the program will run instead of giving a funcall error?

Thank you for your time.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

メ斷腸人バ 2024-12-15 17:33:24

简短回答:如果您想使用 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 中检查这一点。然后,您需要每个子树的递归步骤,并且需要将节点的值推送到结果。执行这些步骤的顺序决定了您是进行前序遍历、中序遍历还是后序遍历。你的代码表明你已经理解了这个原理,所以你只需要让它在 Lisp 代码中工作即可。您可以使用 push 将值推送到 result,并使用 consp 检查节点是否为非空列表。由于对空列表没有任何作用,因此您基本上只需要在 cond 中进行一次测试,但您也可以显式检查节点是否为 null,就像您所做的那样在你的代码中。

Short answer: If you want to use if, note that you'll need a progn in order to have more than one form in the consequent and alternative cases.


Long answer – also explains how to traverse accumulating the visited nodes in a list:

I guess this is homework, so I won't give you a full solution, but your question shows that you have basically the right idea, so I'll show you an easy, idiomatic way to do this.

First, you're right: The car of an unquoted form should be a function, so basically anything like (foo ...), where foo is not a function (or macro, special form ...), and the whole thing is to be evaluated, will be an error. Note that this does not hold inside special forms and macros (like cond, for example). These can change the evaluation rules, and not everything that looks like (foo bar) has to be a form that is to be evaluated by the normal evaluation rules. The easiest example would be quote, which simply returns its argument unevaluated, so (quote (foo bar)) will not be an error.

Now, about your problem:

An easy solution would be to have an accumulator and a recursive helper function that traverses the tree, and pushes the values in the accumulator. Something like this:

(defun pre (node)
  (let ((result (list)))
    (labels ((rec (node)
               (cond (...
                      ...
                      ...))))
      (rec node)
      (nreverse result))))

The labels just introduces a local helper function, which will do the actual recursion, and the outer let gives you an accumulator to collect the node values. This solution will return the result as a list. If you just want to print each nodes value, you don't need the accumulator or the helper function. Just print instead of pushing, and make the helper your toplevel function.

Remember, that you'll need a base case where the recursion stops. You should check for that in the cond. Then, you'll need the recursive steps for each subtree and you'll need to push the node's value to the results. The order in which you do these steps decides whether you're doing pre-, in-, or post-order traversal. Your code shows that you already understand this principle, so you'll just have to make it work in Lisp-code. You can use push to push values to result, and consp to check whether a node is a non-empty list. Since there's nothing to do for empty lists, you'll basically only need one test in the cond, but you can also explicitly check whether the node is null, as you did in your code.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文