Common Lisp:在Paul Graham的书中无法获得解压缩功能

Rog*_*llo 1 lisp common-lisp

Lisp新手在这里.

我正在阅读Paul Graham的书,ANSI Common Lisp.

第38页是一个uncompress功能.它需要一对对列表,其中对中的第一项是一个数字,表示应该有多少第二项.例如,解压缩:

((3 a) (2 b) c)
Run Code Online (Sandbox Code Playgroud)

应该产生这个:

(A A A B B C)
Run Code Online (Sandbox Code Playgroud)

我将uncompress函数输入到Lisp解释器(GCL-2.6.2-ANSI)中,然后像这样测试它:

(uncompress '((3 A) B (2 C) (5 D)))
Run Code Online (Sandbox Code Playgroud)

这产生了以下错误消息:

Error in IF [or a callee]: Too many arguments.

Fast links are on: do (use-fast-links nil) for debugging
Broken at IF.  Type :H for Help.
 1 (Abort) Return to top level.
Run Code Online (Sandbox Code Playgroud)

以下是uncompress功能.我认为我忠实地输入了书中所写的内容.我测试了每一件,每件都看似正常.说实话,我被困住了.我不知道是什么导致了这个错误.我很感激你的帮助.

(defun uncompress (lst)
   (if (null lst)
       nil
       (let (elt (car lst))
            (rest (uncompress (cdr lst))))
        (if (consp elt)
            (append (apply #'list-of elt)
                    rest)
            (cons elt rest))))

(defun list-of (n elt)
   (if (zerop n)
       nil
       (cons elt (list-of (- n 1) elt))))
Run Code Online (Sandbox Code Playgroud)

Rai*_*wig 7

如果使用编辑器缩进工具,代码如下所示:

(defun uncompress (lst)
  (if (null lst)
      nil
    (let (elt (car lst))
      (rest (uncompress (cdr lst))))
    (if (consp elt)
        (append (apply #'list-of elt)
                rest)
      (cons elt rest))))
Run Code Online (Sandbox Code Playgroud)

这样可以更容易地发现此错误.在语法上它是错误的,因为IF它不会超过三种形式.

(defun uncompress (lst)
  (if (null lst)   ; the IF has four subforms, one too many
      nil
    (let (elt (car lst))  ;<- variables ELT and CAR? Makes no sense
      (rest (uncompress (cdr lst))))   ; <- not using the result?
    (if (consp elt)    ; <- fourth form in IF? Does not make sense.
        (append (apply #'list-of elt)
                rest)
      (cons elt rest))))
Run Code Online (Sandbox Code Playgroud)

在Common Lisp的这两个IFLET特殊的运营商内置的语法.

在Lisp中,语法LET通常是:

let ({var | (var [init-form])}*) form* => result*
Run Code Online (Sandbox Code Playgroud)

在Common Lisp中,可以在以下的正文形式之上添加声明LET:

let ({var | (var [init-form])}*) declaration* form* => result*
Run Code Online (Sandbox Code Playgroud)

IFCommon Lisp 的语法是:

if test-form then-form [else-form] => result*
Run Code Online (Sandbox Code Playgroud)

缩进

通常,手动缩进Lisp代码并不是一个好主意.让编辑器或IDE执行此操作.确保所有代码都正确缩进.

如果您有语法问题:首先重新缩进表达式 - >这确保代码正确缩进,然后使查找问题更容易.接下来编译代码并读取编译器错误消息.Common Lisp有很棒的编译器,有些还有很好的错误报告.

代码

无论如何代码都不是很好:它使用递归,其中存在高阶函数或LOOP会更好

这个版本得到了两个:高阶MAPCAN和a LOOP:

(defun uncompress (list)
  (mapcan #'expand-item list))

(defun expand-item (item)
  (typecase item
    (atom (list item))
    (cons (destructuring-bind (n element) item
              (loop repeat n collect element)))))
Run Code Online (Sandbox Code Playgroud)

  • @RogerCostello参见[亚历山大]的[`mappend`](https://common-lisp.net/project/alexandria/draft/alexandria.html#Conses)(https://common-lisp.net/project/alexandria /). (2认同)
  • @coredump:MAPCAN在这里很好,因为列表是新鲜的,可以很好地链接.;-) (2认同)