0
(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's 通过列表 y,递归打印 (a(car y))。如果 P 是第一个参数,y 是列表 (1 2 3),它应该返回 ((P1) (P2) (P3))。但是,当我尝试为其提供参数并执行它时,我无法让该函数工作。这里有什么不正确的?

4

3 回答 3

3

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

它怎么能打印任何东西?函数中没有一条打印语句。

如果 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)
于 2012-12-08T09:08:09.480 回答
1

在你做任何其他事情之前,请先看看可用的介绍性lisp文本。

[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)

首先,这些定义并没有按照您的散文描述所说的那样做;作为递归函数的一部分,您没有任何打印。您 print result,但那是在prefixed 列表全部放在一起之后。您的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>,因为对的尾部不是列表,而 a 的尾部list .


(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)))在定义该函数后在 REPL 进行评估应该会给你

CL-USER> (print (prefix 'a (list 1 2)))

((A . 1) (A . 2)) 
((A . 1) (A . 2))

请注意,我们有重复的输出;这是因为 Lisp REPL 会自动打印您评估的任何表单的返回值,这意味着您实际上可以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))现在应该返回(A1 A2)。请注意,Common Lisp 不区分大小写,会自动将小写符号转换为大写。


递归是一个很好的学习工具,尾递归是 Scheme 中的主要迭代结构,但如果可以的话,迭代地做事通常是个好主意。请注意,CL 不保证尾调用优化,因此这通常意味着使用loopmapcar(或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))

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

CL-USER> (prefix 'a (make-list 100000 :initial-element 1))
Control stack guard page temporarily disabled: proceed with caution
Control stack guard page temporarily disabled: proceed with caution
; Evaluation aborted on #<SB-KERNEL::CONTROL-STACK-EXHAUSTED {1004D674F3}>.

CL-USER> (iterative-prefix 'a (make-list 100000 :initial-element 1))
(A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 A1 ...)

CL-USER>
于 2013-02-12T14:50:59.280 回答
0

两条评论:

首先, cons 返回一个新的 cons 单元格,第一个参数在 cons 中,第二个参数在单元格的 cdr 中。因此 (cons 1 2) 创建了一个新的 cons 单元格 (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 语言的一大优势。

于 2013-02-12T07:59:01.447 回答