我的Lisp代码不会运行,但函数本身会编译

时间:2012-12-08 08:31:44

标签: lisp common-lisp

(defun prefix (a y) (cond ((null y) nil)
    (t (cons (cons a (car y)) (prefix a (cdr y))))))
(setq result prefix(a (cons 1 2)))
(print result)

这个函数cdr通过列表y,递归地打印(a(car y))。如果P是第一个参数,并且y是列表(1 2 3),则它应该返回((P1)(P2)(P3))。但是,当我尝试给它参数并执行它时,我无法使该函数工作。这里有什么不对?

3 个答案:

答案 0 :(得分:3)

  

这个函数cdr通过列表y,递归打印(a(car y))。

怎么打印什么?函数中没有单个print语句。

  

如果P是第一个参数,y是列表(1 2 3),它应该返回((P1)(P2)(P3))。

从查看代码开始,它将返回((P . 1) (P . 2) (P . 3))而不是((P1) (P2) (P3))

  

但是,当我尝试给它参数并执行它时,我无法使该函数工作。这里有什么不对?

你的电话看起来不对劲。 prefix( ... )有些东西看起来不像Lisp。在Lisp中,调用函数的语法是(prefix ... )。函数名称位于左括号后面。

(defun prefix (a y)
  (cond ((null y) nil)
        (t (cons (cons a (car y))
                 (prefix a (cdr y))))))

(setq result prefix(a (cons 1 2)))  ; this line has problems

(print result)

答案 1 :(得分:1)

请查看introductory lisp texts可用before您做其他事情。

[dons code-review hat]

(defun prefix (a y) (cond ((null y) nil)
    (t (cons (cons a (car y)) (prefix a (cdr y))))))

(setq result prefix(a (cons 1 2)))

(print result)

首先,这些定义不符合您的散文描述所说的;作为递归函数的一部分,您没有任何打印。您打印result,但是在prefix ed列表完整地放在一起之后。您的prefix函数也不会返回新符号,而是返回对。


无需设置中间值即可打印它们; Lisp函数隐式返回,可以在没有setq

的情况下编写
(defun prefix (a y) 
  (cond ((null y) nil)
         (t (cons (cons a (car y)) 
              (prefix a (cdr y))))))

(print prefix(a (cons 1 2)))

Lisp完全是前缀标记,函数名称出现在调用括号内。

...

(print (prefix a (cons 1 2)))

a来电中的

prefix是一个未绑定的变量。我认为您要么使用“a符号'a”,要么使用“a”,即:a。如果您只是传递a,Lisp将尝试将该名称作为变量进行评估,如果您没有为其分配值,则会失败。

...

(print (prefix 'a (cons 1 2)))

prefix的第二个参数不是正确的列表;这是一对。为了看到差异,你需要看一下两种结构的底层指针结构。

(cons 1 2) => [ 1 | 2 ]
(list 1 2) => [ 1 |   ]
                    \
                  [ 2 |  ]
                        \
                        NIL

如果您尝试使用函数期望正确列表的对,则会得到错误#<TYPE-ERROR expected-type: LIST datum: 2>,因为对的尾部不是列表,而{{1}的尾部}


list

此时,你已经有了一个可运行的程序。在定义该函数后,在REPL中评估(defun prefix (a y) (cond ((null y) nil) (t (cons (cons a (car y)) (prefix a (cdr y)))))) (print (prefix 'a (list 1 2))) 应该会给你

(print (prefix 'a (list 1 2)))

请注意,我们有重复的输出;这是因为Lisp REPL会自动打印您评估的任何表单的返回值,这意味着您实际上可以完全删除CL-USER> (print (prefix 'a (list 1 2))) ((A . 1) (A . 2)) ((A . 1) (A . 2))

print

为了做你想说的事情(用第一个参数作为前缀创建一个新的符号列表),你实际上需要使用一些字符串操作和CL-USER> (prefix 'a (list 1 2)) ((A . 1) (A . 2)) 来创建符号。

intern

(defun prefix (a y) (cond ((null y) nil) (t (cons (intern (format nil "~a~a" a (car y))) (prefix a (cdr y)))))) 现在应该返回(prefix 'a (list 1 2))。请注意,Common Lisp不区分大小写,自动将小写符号转换为大写。


递归是一个很好的学习工具,尾递归是Scheme中的主要迭代构造,但如果可以的话,迭代地做事情通常是个好主意。请注意,CL不保证尾调优化,因此这通常意味着在某些情况下使用(A1 A2)loop(或mapcar)。

dolist

这些将为您提供与递归版本相同的输出,但如果您为它们提供足够长的列表,则不会有与堆栈空间不足相同的风险。

(defun iterative-prefix (a a-list)
  (loop for elem in a-list
        collect (intern (format nil "~a~a" a elem))))

(defun map-prefix (a a-list)
  (mapcar 
   (lambda (elem) (intern (format nil "~a~a" a elem)))
   a-list))

答案 2 :(得分:0)

两条评论:

首先,cons返回一个新的cons单元格,其中第一个参数位于cons中,第二个参数位于单元格的cdr中。 所以(缺点1 2)创建了一个新的缺陷单元(1.2),这就是你所看到的。

如果你想让结果成为列表(1 2),那么第二个参数必须是一个列表:(cons 1'(2))

其次,你写了一些相当难看的C风格的Lisp代码。考虑:

(defun prefix (a y)
  (if ((null y) nil)
    (cons (cons a (car y)) (prefix a (cdr y)))))
(print (prefix a '( 1 2))))

注意:不要创建仅在以下行中使用的变量。正确布局代码(人们在阅读Lisp时不解析括号,它们依赖于传统布局)。如果只有一个测试加上默认值(即if / then / else),则使用if而不是cond。

您的代码仍然有错误(我还没有修复)。看起来你实际上并没有在REPL中编写这段代码 - 如果你这样做,你会发现编写Lisp代码要容易得多。这是Lisp优于Algol语言的一大优势。