【问题标题】:Is there a format directive to iterate over vectors in Common Lisp?是否有格式指令可以在 Common Lisp 中迭代向量?
【发布时间】:2013-07-30 19:43:05
【问题描述】:

Common Lisp 支持大量的格式化指令。但是,我找不到一个方便的指令来解决我的问题。基本上,我想打印一个数字网格。

使用列表可以很好地工作:

(format t "~{~A|~A|~A~%~^-----~%~}" '(1 2 3 4 5 6 7 8 9))

1|2|3
-----
4|5|6
-----
7|8|9
NIL

我找不到类似的结构来迭代向量。 CLtL2 states clearly that ~{...~} 需要一个列表作为参数。无论如何,我尝试使用向量,但我的 Clisp 正确地惊呼了错误的参数类型。作为一种解决方法,我使用全能的loop 将我的向量转换为一次性列表。

(let ((lst (loop for e across '#(1 2 3 4 5 6 7 8 9) collecting e)))
   (format t "~{~A|~A|~A~%~^-----~%~}" lst))

1|2|3
-----
4|5|6
-----
7|8|9
NIL

这行得通,但我觉得它是一个笨拙的临时解决方案。我宁愿不要只为format 创建大量临时列表。有没有办法直接迭代向量?

出于好奇,format 是否有理由不支持序列?

【问题讨论】:

  • 您可以随时添加自己的(format t "~/func-name/" something)

标签: format common-lisp


【解决方案1】:
(defun pprint-array (stream array
                     &optional colon amp (delimiter #\Space))
  (declare (ignore colon amp))
  (loop
     :with first-time = t
     :for x :across array
     :unless first-time :do (format stream "~C" delimiter) :end
     :do (format stream "~S" x)
     (setf first-time nil)))

(format t "~' :@/pprint-array/" #(1 2 3 4)) ; 1 2 3 4

您可以添加更多参数(它们将用逗号分隔),或者您也可以以某种方式处理冒号和&符号。

按照 Svante 的建议,这里是这个函数的一个稍微改变的版本,它还以下列方式利用冒号和 & 符号:冒号使它在 prin1princ 之间变化,而 at-sign 使它递归地打印嵌套数组 (打印多维数组等可能会更加复杂......但是时间有限,这就是它:

(defun pprint-array (stream array
                     &optional colon amp
                       (delimiter #\Space) (line #\Newline))
  (if amp (loop
             :with first-time = t
             :for a :across array
             :unless first-time
             :do (when line (write-char line stream)) :end
             :if (or (typep a 'array) (typep a 'vector))
             :do (pprint-array stream a colon amp delimiter line)
             :else
             :do (if colon (prin1 a stream) (princ a stream)) :end
             :do (setf first-time nil))
      (loop
         :with first-time = t
         :for x :across array
         :unless first-time
         :do (when delimiter (write-char delimiter stream)) :end
         :do (if colon (prin1 x stream) (princ x stream))
         (setf first-time nil))))

【讨论】:

  • 我相信(没有硬数据,只是这里和那里的一些经验),而不是使用带有由单个指令组成的格式字符串的 format 调用,使用带有适当参数的 write 是性能显着提高。这在这里可能很重要,因为它发生在字符串每个字符的紧密循环中。
【解决方案2】:
  1. 我会使用coerce 而不是loopvectors 转换为lists。
  2. 不会vectors 上使用format+coerce;我会直接迭代vector。这将产生更易读(和更高效)的代码。
  3. format 不支持vectors 的原因可能是历史原因。

【讨论】:

    【解决方案3】:

    您可能正在寻找类似的东西:

    (format t "~{~A|~A|~A~%~^-----~%~}" (coerce #(1 2 3 4 5 6 7 8 9)
                                                'list))
    1|2|3
    -----
    4|5|6
    -----
    7|8|9
    NIL
    

    但我会听 sds 的回答,因为这肯定不是最有效和最易读的方法,而是直接在向量上迭代。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-10-19
      • 1970-01-01
      • 2021-08-22
      • 2016-06-20
      • 1970-01-01
      • 1970-01-01
      • 2013-11-09
      • 1970-01-01
      相关资源
      最近更新 更多