我认为,如果可以的话,生成Python列表或自定义对象而不是字符串,并使用@saulspatz建议的pprint模块是一个合理的方法。
整个问题看起来像是一个XY问题的示例。为什么?因为你正在使用Python(为什么不用Lisp?)来操作表示生成的Lisp风格代码的字符串(为什么不用数据结构?),其中Lisp风格被定义为“一堆括号和文本”。 对于“如何漂亮地打印?”,我的回答是“我不会从这里开始!”。 在你的情况下,除了使用现有的工具之外,避免重复发明轮子的最佳方法是坚持使用简单的输出格式。
但首先,为什么需要漂亮地打印?谁会查看结果代码?
根据你使用的确切Lisp方言和代码的预期用途,你可以非常不同地格式化代码。例如,考虑换行符、缩进和文本的最大宽度。特别是Common Lisp pretty-printer已经非常复杂,我怀疑你不想拥有相同级别的可配置性。
如果你使用Lisp,简单调用pprint
就可以解决你的问题,但你正在使用Python,所以暂时坚持最合理的输出,因为漂亮打印是一个麻烦的问题。
如果你的代码是给人类读者看的,请:
这很丑陋:
( * ( + 3 x )
(f
x
y
)
)
这更好:
(* (+ 3 x)
(f x y))
(* (+ 3 x) (f x y))
点击此处获取更多详细信息。
但在打印之前,您需要解析输入字符串并确保其格式正确。也许由于您生成表单的方式,您确定它是格式良好的,但我认为打印机应该忽略这一点并且不要做太多假设。如果您将 Python 对象表示的 AST 传递给漂亮的打印机而不仅仅是字符串,那么这将更容易,如评论中所建议的。您可以构建数据结构或自定义类,并使用 pprint (python) 模块。如果您可以更改生成 Lisp 风格代码的方式,则如上所述,这似乎是您的选择。
对于字符串,您应该处理任何可能的输入并拒绝无效的输入。这意味着检查括号和引号是否平衡(注意转义字符等)。实际上,您不需要为打印真正构建一个中间树(尽管它可能有助于程序的其他部分),因为 Lisp 风格代码由易于嵌套并使用前缀符号的形式组成:您可以从左到右扫描输入字符串,并在看到括号时按需要打印(左括号:递归;右括号:从递归返回)。当您首次遇到未转义的双引号 "
时,读取直到下一个 "
,... 这个方法与简单的打印方法结合使用,可能已足够满足您的需求。
我认为最简单的方法是使用三引号。如果你这样说:
print """
(((This is some lisp code))) """
应该可以工作。 您可以在三引号内以任何方式格式化代码,它将以您想要的方式呈现。
祝您好运,编码愉快!
我曾经为美化基于Lisp的CLIPS编写了一个简单的漂亮打印机。或许可以帮到你:
def clips_pprint(clips_str: str) -> str:
"""Pretty-prints a CLIPS string.
Indents a CLIPS string for easier visual confirmation during development
and verification.
Assumes the CLIPS string is valid CLIPS, i.e. braces are paired.
"""
LB = "("
RB = ")"
TAB = " " * 4
formatted_clips_str = ""
tab_count = 0
for c in clips_str:
if c == LB:
formatted_clips_str += os.linesep
for _i in range(tab_count):
formatted_clips_str += TAB
tab_count += 1
elif c == RB:
tab_count -= 1
formatted_clips_str += c
return formatted_clips_str.strip()
pprint
模块吗?文档中的示例似乎表明它可能正好符合你的要求。(我假设你已经将其作为S表达式而不是树,正如你的评论所说。) - saulspatz