3

虽然我偶尔会在 Perl 正则表达式中做梦,但 Common Lisp (CLisp) 的格式规范仍然让我有些困惑。我正在拍摄以下结果:

("No Match" (-2378 11 4) (-2378 11 5))给定我想要的列表:

| No Match| -2378 11  4| -2378 11  5|

出另一端。这是我得到的:

[685]> (fss sd)

("No Match" (-2378 11 4) (-2378 11 5))
[686]> (format t "|~{~9<~a~>~2*~}~:*~{~*~{|~6d~3d~3d~}~}|" (fss sd))
| No Match| -2378 11  4
*** - There are not enough arguments left for this format directive.
      Current point in control string:
        "|~{~9<~a~>~2*~}~:*~{~*~{|~6d~3d~3d~}~}|"
                                           |
The following restarts are available:
ABORT          :R1      Abort main loop
Break 1 [687]> :R1

[688]>

我很高兴我已经走了 2/3 的路程,但这种情况让我有点发疯。如果我理解正确,则|~{~9<~a~>~2*~}消耗列表的第一个元素,No Match然后跳过其余部分。下一部分,~:*将参数指针重置回列表的开头。然后~{~}包装器将我放在列表中。接下来~*跳过列表中已处理的部分。下一对~{~}进入参数的第一个子列表。第一个子列表处理正确。然后...错误。显然,我对格式的理解有问题,但我不清楚那可能是什么。

我经常觉得 CL 的其余部分非常简单,但我真的认为我们至少需要CL Cookbook中的“Format Cookbook”一章。

总之,这个有抱负的人需要一个更有知识的追随者的帮助。帮助!

4

2 回答 2

4

我逐步构建了一个解决方案。由于格式字符串只有一个参数,我首先创建了一个格式字符串来打印列表的每个元素:

CL-USER> (format t "~{|~A~}|"
                 '("No Match" (-2378 11 4) (-2378 11 5)))
|No Match|(-2378 11 4)|(-2378 11 5)|

现在,在第一个元素之后,我们实际上想要遍历所有剩余的参数,我们可以用~@{. 我在每个元素周围添加了方括号,以便我们可以看到迭代的边界。

CL-USER> (format t "~{|~A ~@{[~A]~}~}|"
                 '("No Match" (-2378 11 4) (-2378 11 5)))
|No Match [(-2378 11 4)][(-2378 11 5)]|

现在,方括号中列表中的每个元素都需要单独打印,因为字段宽度并不完全相同。我们也可以~A~9<~A~>now 替换首字母。

CL-USER> (format t "~{|~9<~A~>~@{~{|~6d~3d~3d~}~}~}|"
                 '("No Match" (-2378 11 4) (-2378 11 5)))
| No Match| -2378 11  4| -2378 11  5|

现在(在另一个答案中也指出了这一点),~@紧随其后的使用~{是一个可以替换为的构造~:@{,它缩短了格式字符串。

CL-USER> (format t "~{|~9<~A~>~:@{|~6d~3d~3d~}~}|"
                 '("No Match" (-2378 11 4) (-2378 11 5)))
| No Match| -2378 11  4| -2378 11  5|

最后,审美指令,~A可以用来指定字段宽度。 ~mincolA把空格放在右边,但~mincol@A把它们放在左边,所以~<不需要使用。 ~9<~A~>变成~9@A

CL-USER> (format t "~{|~9@A~:@{|~6d~3d~3d~}~}|"
                 '("No Match" (-2378 11 4) (-2378 11 5)))
| No Match| -2378 11  4| -2378 11  5|

这种增量方法可以在 Lisp 中非常频繁地使用,以首先解决部分问题,然后逐步改进解决方案。与其他具有更昂贵的 write-compile-run 周期的语言不同,Lisp 的快速 REPL 使这种过程非常容易。

如果您要使用 进行大量工作format,则值得浏览一下 HyperSpec 中的第22.3 节格式化输出。大多数功能您可能在很长一段时间内都不会使用,但是在浏览了该部分之后,当您需要它们时,它们会在您的脑海中浮现。(那时您必须查阅手册,但通常被低估的一点是,您会知道手册中有一些内容,以及在哪里可以找到它。)

于 2013-06-16T20:54:48.360 回答
1

使用第三个~{,您正在输入第一个子列表的元素打印。每次迭代都会消耗子列表的三个元素,因此它是在单遍之后完成的。然后,退出这个循环,下一个更高的循环进入下一次迭代。它跳过了外部列表的另一个元素(第二个子列表),但是没有任何参数可用于再次进入内部循环。

:您可以简单地同时使用 the和@修饰符~{来处理剩余的列表,而不是向前和向后跳过:

"|~{~9<~a~>~:@{|~6d~3d~3d~}~}|"
于 2013-06-16T20:51:29.303 回答