在Common Lisp中是否有一个格式指令来迭代向量



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明确指出~{...~}期望一个列表作为参数。无论如何,我都尝试过使用向量,但我的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不支持序列是有原因的吗?

(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符号使它递归地打印嵌套数组(也可以更复杂地打印多维数组等……但时间有限,它是这样的:

(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))))
  1. 我将使用coerce而不是loop来将vectors转换为lists
  2. 我将vectors上使用format+coerce;我会直接迭代vector。这将产生可读性更强(效率更高)的代码
  3. format不支持vector的原因可能是历史原因

您可能正在寻找以下内容:

(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的回答,因为这肯定不是最有效和可读的方法,并直接迭代向量。

最新更新