Formatting 我怎样才能正确地表达我的;格式";功能?

Formatting 我怎样才能正确地表达我的;格式";功能?,formatting,common-lisp,Formatting,Common Lisp,我有一个功能: (defun play (&rest args) (format nil "play ~A~{, ~A~}~%" (car args) (cdr args)))) 而且,据我所知,应该这样使用: (play 1 2 3) (format t "~:[not playing anything~;play ~{~a~^, ~}~]" args args) 在本例中,返回“播放1 2 3”。不幸的是,此代码中有一些错误,因此我的

我有一个功能:

(defun play (&rest args)
  (format nil "play ~A~{, ~A~}~%" (car args) (cdr args))))
而且,据我所知,应该这样使用:

(play 1 2 3)
(format t "~:[not playing anything~;play ~{~a~^, ~}~]" args args)
在本例中,返回“播放1 2 3”。不幸的是,此代码中有一些错误,因此我的emacs编辑器返回以下内容:

main.lisp:7:7:
  error: 
    don't know how to dump #<SWANK/GRAY::SLIME-OUTPUT-STREAM {1004139673}> (default MAKE-LOAD-FORM method called).
    ==>
      #<SWANK/GRAY::SLIME-OUTPUT-STREAM {1004139673}>
    
  note: The first argument never returns a value.
  note: 
    deleting unreachable code
    ==>
      "play ~A~{, ~A~}~%"
main.lisp:7:7:
错误:
不知道如何转储#(调用默认的MAKE-LOAD-FORM方法)。
==>
#
注意:第一个参数从不返回值。
注:
删除无法访问的代码
==>
“播放~A~{,~A~}~%”

你能帮我写这个函数吗?

你的例子对我也很有用(尽管它是人为的,请看注释)

但是,这里有一个使用外部库的示例,如果/当您不记得格式指令时

;; (ql:quickload "str")
(format t "play ~a" (str:join ", " (list 1 2 3)))
;; => play 1, 2, 3

你的例子也适用于我(尽管它是人为的,请参见注释)

但是,这里有一个使用外部库的示例,如果/当您不记得格式指令时

;; (ql:quickload "str")
(format t "play ~a" (str:join ", " (list 1 2 3)))
;; => play 1, 2, 3
错误 在代码中,您可以编写:

(defun play (&rest args)
  (format #.*standard-output* "play ~A~{, ~A~}~%" (car args) (cdr args))))
在对代码求值之前,首先读取代码:将源代码转换为抽象语法树,以lisp值表示。遇到
#。*标准输出*
,读取器计算下一个表单,这里是
*标准输出*
:读取表单时绑定到此变量的实际流存储在结构化表达式中,该表达式是函数
play
的源代码

您可以在代码中放入任意数据,特别是字符串、数字等。在这里,您的源代码包含一个流实例。当您尝试执行函数
play
时,这可能是一个问题,因为在您计算函数体时,流可能已关闭;看起来您正试图修复要将输出写入的流,但这可能不是一个好方法。如果您编辑您的问题以添加更多相关信息,我们可能会找到更好的方法来解决您的问题

在编译代码时,存储流对象也是一个问题,这可能是这里出现此特定错误的原因

如注释中所述,编译文件时会发生这种情况。如果使用Slime,当环境使用临时文件编译缓冲区时,可能会发生这种情况

因此,在使用时会发生错误;特别注意规范中的本段:

要由文件编译器编译的程序必须只包含可外部化的对象;有关此类对象的详细信息,请参见第3.2.4节(编译文件中的文字对象)。有关如何扩展可外部化对象集的信息,请参阅函数make load form和第3.2.4.4节(可外部化对象的附加约束)

文件编译器生成一个对象文件(fasl扩展名)。某些文字值(如常量字符串等)必须存储在生成的文件中,以便在加载回文件()时可以重构类似的对象。对于大多数标准类型,Lisp编译器知道如何转储对象并将其加载回

但是在这里,编译器不知道如何在编译过程中转储
stream
类型的值。您可以为定义方法,但某些数据本身很难(反)序列化:我们如何存储线程或任何其他操作系统资源

格式 除了在读取时评估
*标准输出*
之外,该格式有效

但是,您可以将列表分解为其
car
cdr
,但是:

  • 不检查列表中是否有任何元素;如果
    args
    为NIL,则打印文本为
    “play NIL”
    ,这与一个元素NIL的列表不明确;或者,您可能只使用非空列表调用函数,在这种情况下,您可能应该添加
    (check type list cons)
    ,以便更好地捕获编程错误。或者,定义
    play
    ,如下所示:

    (defun play (arg &rest more-args)
      ...)
    
    然后,
    ARG
    是car,
    MORE-ARGS
    是cdr,用户不能使用空的ARGS列表调用函数(约束在函数签名中也更明确)

  • 您可以分解它们来处理元素之间的逗号添加,这是
    format
    本身已经可以处理的事情

    (format t "~{~a~^, ~}" args)
    
    如果正在处理的列表中没有剩余元素,则插入符号操作符将以格式退出当前迭代上下文。所以在这里,我们只在列表中有更多元素时添加逗号和空格。 此外,您可能希望处理空列表并打印其他内容,如
    如果列表为空,则不播放任何内容
    ;在这种情况下,使用以下构造:

    "~:[  ...  ~;   ...  ~]"
    
    带有冒号修饰符的方括号运算符类似于if,即
    ~之前的部分
    用于NIL情况,即
    ~用于非零部件;您需要这样写:

    (play 1 2 3)
    
    (format t "~:[not playing anything~;play ~{~a~^, ~}~]" args args)
    
    请注意如何两次提供
    args
    :首先用于测试,然后(仅)在测试的真正分支中使用它。通过让format在其参数列表上倒带,我们可以做得更好,这样它就可以使用单个参数
    args
    两次:

    (format t "~:[not playing anything~;~:*play ~{~a~^, ~}~]" args)
    
    带有冒号修饰符
    ~:*
    的星号运算符通过返回所提供参数列表中的一个参数来更改
    格式
    正在使用的当前参数

  • 您可以在此处阅读有关格式的更多信息:

    错误 在代码中,您可以编写:

    (defun play (&rest args)
      (format #.*standard-output* "play ~A~{, ~A~}~%" (car args) (cdr args))))
    
    在对代码求值之前,首先读取代码:将源代码转换为抽象语法树,以lisp值表示。遇到
    #。*标准输出*
    ,读取器计算下一个表单,这里是
    *标准输出*
    :读取表单时绑定到此变量的实际流存储在结构化表达式中,该表达式是函数
    play
    的源代码

    Y