FORMAT是否为列表迭代提供计数器

时间:2015-06-25 15:05:30

标签: common-lisp

我经常想要输出列表并在列表中打印它们的位置,例如

'(a b c)将成为"1:A 2:B 3:C"

由于FORMAT已经支持迭代给定列表,我想知道它是否也提供某种计数指令?

E.g。 FORMAT字符串可能如下所示:"~{~@C:~a~}"~@C则是计数器。

2 个答案:

答案 0 :(得分:5)

如果您想要一个无聊的答案,请转到:

(format T "~:{~a:~a ~}" (loop for i from 0 for e in '(x y z) collect (list i e)))

现在换一个更有趣的!与@ Renzo的答案类似,它使用Tilde指令来实现其工作。

(defvar *count* 0)
(defvar *printer* "~a")

(defun iterate-counting (stream arg c at)
  (declare (ignore c))
  (let ((*count* (if at -1 0)))
    (destructuring-bind (*printer* delimiter &rest args) arg
      (format stream (format NIL "~~{~~/iterate-piece/~~^~a~~}" delimiter) args))))

(defun iterate-piece (stream arg &rest dc)
  (declare (ignore dc))
  (incf *count*)
  (format stream *printer* *count* arg))

这使用两个特殊变量使其既可以是线程安全的,也可以允许嵌套。我不会说使用它很方便。列表参数的第一项必须是格式字符串,表示如何打印参数和计数器。对于这样的格式列表,第一个参数是计数器,第二个参数是要列出的实际项目。如果您需要使用asterisk directive,可以切换它们。第二项应该是一个字符串,作为每个项目之间的分隔符打印。最后,列表的其余部分必须是要打印的实际项目。

(format T "~/iterate-counting/" '("~a:~a" " " x y z))
  => 1:X 2:Y 3:Z
(format T "~/iterate-counting/" '("~a:~/iterate-counting/" " " ("~a>~a" "," 0 1 2) ("~a>~a" "," a b c) ("~a>~a" "," x y z)))
  => 1:1>0,2>1,3>2 2:1>A,2>B,3>C 3:1>X,2>Y,3>Z

如果您希望它从零开始计数,请为@添加iterate-counting修饰符:

(format T "~@/iterate-counting/" '("~a:~a" " " x y z))
  => 0:X 1:Y 2:Z

我不会亲自使用这个,因为如果你偶然发现这个指令,那么发生的情况远非如此明显。对于潜在的未来读者而言,为此编写量身定制的功能可能会比尝试使用format更加困惑。

答案 1 :(得分:2)

生成编号列表的一种不那么简单但可重用的方法是使用~/指令(Tilde Slash: Call Function)和用户定义的函数。例如:

(let ((position 0))
  (defun init-pos(str arg col at)
    (declare (ignore str arg col at))
    (setf position 0))
  (defun with-pos(str arg col at)
    (declare (ignore col at))
    (format str "~a:~a" (incf position) arg)))

然后写这样的格式:

(format nil "~/init-pos/~{~/with-pos/~^ ~}" nil '(a b c))

请注意,正如评论中所述,此解决方案有两个限制:

  1. 如果需要在并发线程中格式化对象,
  2. ,则无法使用它
  3. 您不能将其用于嵌套列表。