首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >定义编译时已知的结构的常量数组

定义编译时已知的结构的常量数组
EN

Stack Overflow用户
提问于 2021-12-26 13:32:55
回答 2查看 139关注 0票数 0

在我的程序中,我有常量字符串,这些值在编译时是已知的。对于每个偏移量,当前有两个相关联的字符串。我首先编写了以下代码:

代码语言:javascript
复制
(eval-when (:compile-toplevel :load-toplevel :execute) ;; BLOCK-1
  (defstruct test-struct
    str-1
    str-2))

(eval-when (:compile-toplevel) ;; BLOCK-2

  (defparameter +GLOBAL-VECTOR-CONSTANT+ nil) ;; ITEM-1

  (let ((vector (make-array 10
                            :initial-element (make-test-struct)
                            :element-type    'test-struct)))
    (setf (test-struct-str-1 (aref vector 0)) "test-0-1")
    (setf (test-struct-str-2 (aref vector 0)) "test-0-2")

    (setf +GLOBAL-VECTOR-CONSTANT+ vector)))

(format t "[~A]~%" (test-struct-str-1 (elt +GLOBAL-VECTOR-CONSTANT+ 0)))
(format t "[~A]~%" (test-struct-str-2 (elt +GLOBAL-VECTOR-CONSTANT+ 0)))

这似乎有效,因为它返回以下内容:

代码语言:javascript
复制
[test-2-1]
[test-2-2]

BLOCK-1中,定义了包含数据的struct,用于compile-timeload-timeexecute-time。在BLOCK-2中,在compile-time上执行创建向量和设置值的代码。

但我有以下忧虑:

这些字符串存储在structure

  • I中,需要手动设置每个值的offset ((aref vector 0)(aref vector 1)等)。当我将< code >D21设置为BLOCK-1而不是BLOCK-2时,出现了d24中的错误,而我不理解H 225F 226>时,出现了错误。

在Common Lisp中定义复杂常数的惯用方法是什么?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-12-27 13:01:55

从你的问题上看不清楚你想做什么。

第一个重要注意事项:您的代码被严重破坏了。因为您只在编译时定义了+global-vector-constant+,但是引用它的时间比它晚,所以它失败了。如果编译此文件,然后将编译后的文件加载到冷映像中,则会出现错误。

在处理这样的事情时,确保代码在冷Lisp中编译是绝对关键的。与Interlisp的方式相比,常驻环境的一个经典问题(与Interlisp的方式相比)是无法冷构建的系统:我很确定我在Interlisp系统中工作了几年,没有人知道如何冷构建。

如果您想要的是一个对象(例如,一个数组),它的初始值在编译时计算,然后作为一个文字来处理,那么对它的回答通常是宏:宏正是在编译时执行其工作的函数,因此宏可以扩展为文字。此外,您想要成为文本的对象必须是可外化的(这意味着“可以在编译后的文件中转储”),并且在编译时就知道了其中涉及的任何事情。某些类的实例在默认情况下是可外化的,其他一些类的实例可以通过用户代码进行外部化,而有些类的实例则完全不能外部化(例如函数)。

在很多简单的情况下,比如您给出的例子,如果我理解它,您实际上并不需要宏,而且实际上您几乎总是可以不使用宏,尽管如果使用宏,代码可能会变得更容易理解。

下面是一个简单的例子:如果许多数组的元素是

代码语言:javascript
复制
(defparameter *my-strings*
  #(("0-l" . "0-r")
    ("1-l" . "1-r")))

这意味着*my-strings*将绑定到字符串的字符串数组中。

一个更有趣的例子是元素是什么时候,例如结构。结构也是可以外化的,所以我们可以这样做。事实上,它仍然可以避免宏,尽管它现在变得有点嘈杂。

代码语言:javascript
复制
(eval-when (:compile-toplevel :load-toplevel :execute)
  (defstruct foo
    l
    r))

(defparameter *my-strings*
  #(#s(foo :l "0-l" :r "0-r")
    #s(foo :l "1-l" :r "1-r")))

请注意,以下内容不起作用:

代码语言:javascript
复制
(defstruct foo
  l
  r)

(defparameter *my-strings*
  #(#s(foo :l "0-l" :r "0-r")
    #s(foo :l "1-l" :r "1-r")))

它将无法工作,因为在编译时,您正在尝试将尚未定义的结构的实例外部化(但如果Lisp不冷,您甚至可以重新加载所编译的文件)。同样,在这种情况下,通过确保在加载带有eval-when的文件之前编译和加载定义foo结构的文件,可以避免在更大的系统中使用defparameter

即使在更复杂的情况下,也可以使用宏来逃避。例如,对于许多通常不可外化的对象,您可以教系统如何将它们外部化,然后使用#.将对象合并为文字。

代码语言:javascript
复制
(eval-when (:compile-toplevel :load-toplevel :execute)
  ;; Again, this would be in its own file in a bigger system
  (defclass string-table-wrapper ()
    ((strings)
     (nstrings :initform 0)))

  (defmethod initialize-instance :after ((w string-table-wrapper)
                                         &key (strings '()))
    (let ((l (length strings)))
      (when l
        (with-slots ((s strings) (n nstrings)) w
          (setf s (make-array l :initial-contents strings)
                n l)))))

  (defmethod make-load-form ((w string-table-wrapper) &optional environment)
    (make-load-form-saving-slots w :slot-names '(strings nstrings)
                                 :environment environment))
  )                                     ;eval-when

(defgeneric get-string (from n)
  (:method ((from string-table-wrapper) (n fixnum))
   (with-slots (strings nstrings) from
     (assert (< -1 n nstrings )
         (n)
       "bad index")
     (aref strings n))))

(defparameter *my-strings*
  #.(make-instance 'string-table-wrapper
                   :strings '("foo" "bar")))

当然,请注意,尽管*my-strings*的值是一个文字,但是在加载时运行代码来重建这个对象。但情况总是如此:只是在这种情况下,您必须定义需要运行的代码。与其使用make-load-form-saving-slots,您还可以自己动手,例如通过这样的方法:

代码语言:javascript
复制
(defmethod make-load-form ((w string-table-wrapper) &optional environment)
   (declare (ignore environment))
   (if (slot-boundp w 'strings)
       (values
        `(make-instance ',(class-of w))
        `(setf (slot-value ,w 'strings)
               ',(slot-value w 'strings)
               (slot-value ,w 'nstrings)
               ,(slot-value w 'nstrtrings)))
     `(make-instance ',(class-of w))))

但是make-load-form-saving-slots要容易得多。

下面是一个宏可能最不容易读取代码的示例。

假设您有一个函数从文件中读取字符串数组,例如:

代码语言:javascript
复制
(defun file-lines->svector (file)
  ;; Needs CL-PPCRE
  (with-open-file (in file)
    (loop
       with ltw = (load-time-value
                   (create-scanner '(:alternation
                                     (:sequence
                                      :start-anchor
                                      (:greedy-repetition 1 nil
                                       :whitespace-char-class))
                                     (:sequence
                                      (:greedy-repetition 1 nil
                                       :whitespace-char-class)
                                      :end-anchor)))
                   t)
       for nlines upfrom 0
       for line = (read-line in nil)
       while line
       collect (regex-replace-all ltw line "") into lines
       finally (return (make-array nlines :initial-contents lines)))))

然后,如果此函数在宏扩展时可用,则可以编写以下宏:

代码语言:javascript
复制
(defmacro file-strings-literal (file)
  (check-type file (or string pathname) "pathname designator")
  (file-lines->svector file))

现在我们可以创建字符串的文字向量:

代码语言:javascript
复制
(defparameter *fl* (file-strings-literal "/tmp/x"))

然而,您完全可以这样做:

代码语言:javascript
复制
(defparameter *fl* #.(file-lines->svector "/tmp/x"))

它将做同样的事情,但稍微早一点(在读取时,而不是在宏扩展/编译时)。所以这并没有真正的收获。

但你也可以这么做

代码语言:javascript
复制
(defmacro define-stringtable (name file &optional (doc nil docp))
  `(defparameter ,name ,(file-lines->svector file)
     ,@(if docp (list doc) nil)))

现在你的代码读起来就像

代码语言:javascript
复制
(define-stringtable *st* "my-stringtable.dat")

这实际上是一个重大的进步。

最后,请注意,在file-lines->svector中,load-time-value准确地用于在加载时创建扫描仪一次,这是一个相关的技巧。

票数 2
EN

Stack Overflow用户

发布于 2021-12-26 16:17:09

首先,可以将let代码简化为

代码语言:javascript
复制
(defparameter +global-vector-constant+
  (let ((vector ...))
    ...
    vector))

第二,你也可以

代码语言:javascript
复制
(defparameter +global-vector-constant+
  (make-array 10 :element-type 'test-struct :initial-content
              (cons (make-test-struct :str-1 "test-0-1" :str-2 "test-0-2")
                    (loop :repeat 9 :collect (make-test-struct)))))

请注意,:element-type 'test-struct的好处通常仅限于代码自文档(参见upgraded-array-element-type)。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70486848

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档