LISP-字节数组的快速输出

时间:2018-08-02 16:00:28

标签: output character common-lisp cons

我正在为LISP语言编写一个编译器,而总体目标是使编译器从原始语言生成LISP代码。尝试测量生成的代码的性能时,我发现在打印字符串时严重缺少它。

在原始语言中,字符是字节-算术值,因此字符串是字节数组,并且字节的值对应于其值为字节的ASCII码的字符。 “可打印”字节数组必须为空终止。因此,要打印一个字节数组作为字符串,我必须在打印之前将原始数组的元素映射为字符。处理此问题的函数如下:

(defun writeString (X &AUX (NPOS 0) (i 0))
  (declare (type (simple-VECTOR fixnum *) x))
  (declare (type fixnum NPOS i))
  (SETF NPOS (POSITION 0 X))
  (IF (NOT NPOS)
    (SETF NPOS (LENGTH X)))
  (princ (MAKE-ARRAY NPOS
                     :INITIAL-CONTENTS (map 'vector
                                            #'code-char
                                            (SUBSEQ X 0 NPOS))
                     :ELEMENT-TYPE 'base-char)))  

并将其注入到生成的代码中。

运行带有time的示例代码,我发现princ部分在执行过程中会产生很多麻烦,这会使事情变慢。当放置make-array...位置的静态字符串时,不会降低速度,也不会产生干扰,因此我想这是造成损坏的部分。

在编译时,我已经设置了全速标志,现在在生成的代码中将字节值声明为fixnum。

谁能指出我一种更好的方式来将我的字节数组打印为字符串,同时又避免了过多的困扰?

我可以从一开始就将字节存储为字符,但这会导致语言的某些部分由于需要转换而将它们视为数字变慢。

2 个答案:

答案 0 :(得分:6)

您的代码中的问题

您的代码:

(defun writeString (X &AUX (NPOS 0) (i 0))
  (declare (type (simple-VECTOR fixnum *) x))
  (declare (type fixnum NPOS i))
  (SETF NPOS (POSITION 0 X))
  (IF (NOT NPOS)
    (SETF NPOS (LENGTH X)))
  (princ (MAKE-ARRAY NPOS
                     :INITIAL-CONTENTS (map 'vector
                                            #'code-char
                                            (SUBSEQ X 0 NPOS))
                     :ELEMENT-TYPE 'base-char)))

代码中有几个错误:

  • i未使用
  • 第一种类型声明在语法上无效
  • NPOS的声明错误。您将其定义为FIXNUM,但可以为NIL。

有很多编程错误:

  • 如果只需要输出字符,则无需分配任何数组。
  • 即使您要生成一个数组,也只能执行一次
  • X不是字符串的好名字

一个简单的解决方案:

(defun writestring (bytestring)
  (loop for byte across bytestring
        while (plusp byte)
        do (write-char (code-char byte))))

声明类型的版本可能是:

(defun writestring (bytestring)
  (declare (vector bytestring))
  (loop for byte of-type (integer 0 255) across bytestring
        while (plusp byte)
        do (write-char (code-char byte))))

代替(integer 0 255)的人也可以使用(unsigned-byte 8)

关于生成向量:

让我们看看您如何尝试创建数组:

使用另一个数组中的内容,使用make-array创建一个数组。 为什么不告诉MAP生成正确的数组?

CL-USER 46 > (map '(vector base-char) #'code-char #(102 111 111 98 97 114))
"foobar"

现在,如果由于某种原因要分配数组:

  • 做一次
  • 将内容映射到生成的数组中。为此使用map-into。它将以较短的顺序停止。

示例:

CL-USER 48 > (let ((bytestring #(102 111 111 98 97 114 0 100 100 100)))
               (map-into (make-array (or (position 0 bytestring)
                                         (length bytestring))
                                     :element-type 'base-char)
                         #'code-char
                         bytestring))
"foobar"

答案 1 :(得分:3)

您可以依靠write-sequence,希望可以对其进行优化以写入字符或字节序列。它还接受一个:end参数,该参数对于定界所写字符串的结尾很有用。

我怀疑您是否真的需要使用文字向量(始终为simple-vector),但是如果是这样,则可能需要更改它们。您可以在阅读时进行:

(let ((input #.(coerce #(102 111 111 98 97 114 0 100 100 100)
                       '(vector (mod 256)))))
  (write-sequence (map '(vector base-char)
                       #'code-char
                       input)
                  *standard-output*
                  :end (position 0 input)))

我从没有使用过以下内容,但是您也可以在字符和字节模式下打开相同的文件,并在必要时进行切换:

(with-open-file (out-c #P"/tmp/test"
                      :if-exists :supersede
                      :direction :output)
  (with-open-file (out-8 #P"/tmp/test"
                         :element-type '(unsigned-byte 8)
                         :direction :output
                         :if-exists :append)
    (format out-c "Hello [")
    (file-position out-8 (file-position out-c))
    (write-sequence #(102 111 111 98 97 114) out-8)
    (file-position out-c (file-position out-8))
    (format out-c "]")))

它在/ tmp / test中打印"Hello [foobar]",并且似乎可以处理多字节字符,但是您可能需要进行更多测试。