FORMAT 是否为列表迭代提供计数器
Does FORMAT provide a counter for lists iteration
我经常想输出列表并打印它们在列表中的位置例如
'(a b c)
会变成 "1:A 2:B 3:C"
由于FORMAT
已经支持遍历给定的列表,我想知道它是否也提供某种计数指令?
例如FORMAT
字符串可能如下所示:"~{~@C:~a~}"
而 ~@C
将是计数器。
生成编号列表的一种不那么简单但可重复使用的方法是使用带有用户定义函数的 ~/
指令 (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))
请注意,如评论中所述,此解决方案有两个限制:
- 如果需要在并发线程中格式化对象,则不能使用它,并且
- 您不能将它用于嵌套列表。
如果你想要一个无聊的答案,给你:
(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
我个人不会使用它,因为如果您偶然发现未启动的指令,它会发生什么事情就不太明显了。与尝试 ab/use format
.
相比,为潜在的未来 reader 编写一个定制函数可能会更容易混淆。
我经常想输出列表并打印它们在列表中的位置例如
'(a b c)
会变成 "1:A 2:B 3:C"
由于FORMAT
已经支持遍历给定的列表,我想知道它是否也提供某种计数指令?
例如FORMAT
字符串可能如下所示:"~{~@C:~a~}"
而 ~@C
将是计数器。
生成编号列表的一种不那么简单但可重复使用的方法是使用带有用户定义函数的 ~/
指令 (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))
请注意,如评论中所述,此解决方案有两个限制:
- 如果需要在并发线程中格式化对象,则不能使用它,并且
- 您不能将它用于嵌套列表。
如果你想要一个无聊的答案,给你:
(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
我个人不会使用它,因为如果您偶然发现未启动的指令,它会发生什么事情就不太明显了。与尝试 ab/use format
.