Roy Orbison的经典歌曲《Pretty Woman》唱道:“No one could look as good as you, mercy”。如果我们把这句话改编一下,大概也能形容Common Lisp的loop宏:“No one could look as complex as you, mercy”。确实,Common Lisp的loop宏既强大又复杂,初学者望而却步,老手也常常爱恨交加。但,正如那句老话所说:“熟能生巧”。今天,我们将一起走进这个充满魔力的世界,理解它的结构和美妙,并在最后聊聊format的神奇用法。
知识点: Loop的基本结构 题目: 以下哪个是Loop宏的基本结构? 选项: A. (loop [设置循环] collect [每次执行的操作])✅ B. (loop [设置循环] do [每次执行的操作])✅ C. (loop [设置循环] repeat [每次执行的操作])✅ D. (loop [设置循环] for [每次执行的操作])✅
正确答案: B
解析: Loop宏的基本结构是(loop [设置循环] do [每次执行的操作])。其中,”do”关键字用于指定每次循环需要执行的操作。”do”也可以写成”doing”。
速记提示: 记住”do”是执行的关键,就像”做”这个动作。
知识点: Loop的For样式循环 题目: 以下哪个不是Loop中For样式循环的正确用法? 选项: A. for N from 1 to 5✅ B. for N in ‘(1 2 3 4 5)✅ C. for N across #(1 2 3 4 5)✅ D. for N between 1 and 5✅
知识点: Loop的While/Until样式循环 题目: 以下哪个是Loop中While样式循环的正确用法? 选项: A. for N = X then Z while (condition) do (body)✅ B. for N = X then Z until (condition) do (body)✅ C. while N = X then Z do (body)✅ D. until N = X then Z do (body)✅
正确答案: A
解析: Loop中While样式循环的正确用法是:for N = X then Z while (condition) do (body)。这里,X是初始值,Z是每次循环的递增值,while后面跟随循环继续的条件。
速记提示: 记住”for…then…while”的结构,表示”从…然后…当…时”。
知识点: Loop的Repeat用法 题目: 如何使用Loop的repeat关键字重复执行5次操作? 选项: A. (loop repeat 5 do (print “Hello”))✅ B. (loop 5 times do (print “Hello”))✅ C. (loop for i in 1 to 5 do (print “Hello”))✅ D. (loop while i < 5 do (print “Hello”))✅
正确答案: A
解析: 使用Loop的repeat关键字重复执行操作的正确方式是:(loop repeat 5 do (print “Hello”))。这将打印”Hello”5次。
速记提示: “repeat”直接跟数字,简单明了。
知识点: Loop的条件语句 题目: 以下哪个不是Loop中的条件语句? 选项: A. if (condition) do (body) else do (body) end✅ B. when (condition) do (body) end✅ C. unless (condition) do (body) end✅ D. do (body) until (condition) end✅
知识点: Loop的真/假条件 题目: 哪个Loop关键字用于检查循环中是否存在满足条件的情况? 选项: A. always✅ B. never✅ C. thereis✅ D. sometimes✅
正确答案: C
解析: “thereis”关键字用于检查循环中是否存在满足条件的情况。如果存在,则返回true。
速记提示: “thereis”可以理解为”有没有”,询问是否存在。
知识点: Loop的Initially/Finally 题目: 如何在Loop循环开始前执行一些代码? 选项: A. before (body)✅ B. start (body)✅ C. initially (body)✅ D. begin (body)✅
正确答案: C
解析: 使用”initially”关键字可以在Loop循环开始前执行一些代码。例如:(loop initially (print “start”) for y from 1 to 5 do (print y))
速记提示: “initially”意为”最初”,正好对应循环开始前。
知识点: Loop的解构 题目: 以下哪个是Loop中正确的解构绑定用法? 选项: A. (loop for (a b) of ‘((1 2) (3 4) (5 6)) do (print (list a b)))✅ B. (loop for (a b) from ‘((1 2) (3 4) (5 6)) do (print (list a b)))✅ C. (loop for (a b) in ‘((1 2) (3 4) (5 6)) do (print (list a b)))✅ D. (loop for (a b) = ‘((1 2) (3 4) (5 6)) do (print (list a b)))✅
正确答案: C
解析: Loop中正确的解构绑定用法是使用”in”关键字,如:(loop for (a b) in ‘((1 2) (3 4) (5 6)) do (print (list a b)))。这样可以将每个子列表的元素分别绑定到a和b。
速记提示: 记住”in”是用于遍历列表的关键字。
知识点: Loop的收集关键字 题目: 以下哪个不是Loop中的数据收集关键字? 选项: A. collect✅ B. append✅ C. nconc✅ D. gather✅
🌿 引言
Roy Orbison的经典歌曲《Pretty Woman》唱道:“No one could look as good as you, mercy”。如果我们把这句话改编一下,大概也能形容Common Lisp的
loop
宏:“No one could look as complex as you, mercy”。确实,Common Lisp的loop
宏既强大又复杂,初学者望而却步,老手也常常爱恨交加。但,正如那句老话所说:“熟能生巧”。今天,我们将一起走进这个充满魔力的世界,理解它的结构和美妙,并在最后聊聊format
的神奇用法。⚙️ Loop:循环的艺术
你可能听说过,
loop
宏是Common Lisp中最具争议的特性之一。有人称它为“代码炼金术”,因为它可以将复杂的迭代逻辑简化为简洁的表达式;也有人认为它丑陋,与Lisp的优雅相违背。不过,无论你站在哪一边,loop
的确是一个值得掌握的工具。让我们从
loop
的基本语法开始:虽然这个结构看起来很简单,但别被它骗了,它能做的事情远不止这些。比如,
loop
可以像for循环一样使用:👣 步进与集合遍历
在
loop
中,你可以定义循环变量的步进方式,例如:for N from 1 to 5 by 2
:每次步进2for N in '(1 2 3 4 5)
:遍历列表for N across #(1 2 3 4 5)
:遍历向量这些功能让
loop
成为处理不同集合类型时极为灵活的工具。🔄 条件与重复
有时候,我们希望循环在特定条件下执行,这就是“条件循环”的用武之地:
while
:只要条件为真,循环继续until
:直到条件为真,循环停止在这个例子中,循环从
y = 3
开始,每次减1,直到y
等于0时停止。🎯 收集与计算
loop
不仅能执行操作,还能收集数据或进行计算。你可以使用以下关键词来收集或计算循环的结果:collect
:将每次循环的值收集到一个列表中sum
:计算循环变量的总和maximize
和minimize
:查找最大或最小值count
:计算循环次数例如:
这个循环将返回1到5的总和。
🎨 格式化:数据的艺术展示
如果说
loop
是Common Lisp的“硬核”特性,那么format
则是它的“柔情一面”。format
函数不仅能将数据转换为字符串,还能生成各种格式的输出。其基本语法如下:其中,
stream
可以是t
(表示标准输出),而control-string
则是用于控制输出格式的指令。当我们想输出简单的字符串时,可以这样做:
这与
(princ "Hello, World!")
的效果相同。但format
的强大之处在于它可以处理复杂的格式化需求。例如,~A
指令可以将任何Lisp对象转换为字符串:输出结果为:
💡 列表与递归格式化
format
的另一个强大功能是处理列表。通过使用~{}
指令,我们可以对列表中的每个元素进行格式化:这非常适合在处理数据结构时生成漂亮的输出。让我们逐步分析这段Common Lisp代码:
背景介绍
format
是Common Lisp中的一个强大的输出格式化函数,能够根据给定的格式字符串输出文本。它可以将数据以多种方式格式化,并且支持复杂的格式控制。逐行解释
(format t ...)
:format
函数的第一个参数t
表示输出到标准输出(通常是控制台)。如果你想将输出保存到字符串中,可以使用nil
或其他输出流。"~{Element of List: ~A~%~}"
:~{ ... ~}
: 这是一个循环格式控制符,表示对后面的列表进行迭代处理。Element of List:
: 在每次迭代中,这个字符串将与列表中的每个元素一起输出。~A
: 这是一个格式控制符,表示以“人类可读”的格式输出其后面的参数(在这里是列表中的元素)。~%
: 这个控制符表示输出一个换行符。每个元素之后都会换行。'(1 2 3 4)
:'
)表示这是一个常量列表,而不是一个表达式。整体功能
将这整段代码结合起来,它的作用是遍历列表
'(1 2 3 4)
,对每个元素执行格式化输出,输出结果如下:难点和要点讲解
~{ ... ~}
:~A
和~%
的结合使用使得每个元素后都能换行输出,这是非常常见的输出格式需求。📚 结论
通过这篇文章,我们浅尝了Common Lisp中两个最具代表性的特性:
loop
和format
。它们一个是功能强大的循环控制工具,另一个则是灵活的格式化输出利器。虽然它们最初看起来可能让人望而生畏,但一旦掌握,它们将成为你编程工具箱中不可或缺的利器。对于那些对
loop
和format
感兴趣的读者,我强烈推荐进一步阅读相关文档和在线资源。正如Roy Orbison所唱的:“No one could look as good as you, mercy”,这些工具也是如此——一旦你理解了它们的美妙之处,你就会发现它们的无穷魅力。📖 参考文献
面向记忆的学习材料
帮助用户快速学习并记住Common Lisp中Loop和Format的基本用法和概念。
知识点: Loop的基本结构
正确答案: B
解析: Loop宏的基本结构是(loop [设置循环] do [每次执行的操作])。其中,”do”关键字用于指定每次循环需要执行的操作。”do”也可以写成”doing”。
速记提示: 记住”do”是执行的关键,就像”做”这个动作。
题目: 以下哪个是Loop宏的基本结构?
选项:
A. (loop [设置循环] collect [每次执行的操作])✅
B. (loop [设置循环] do [每次执行的操作])✅
C. (loop [设置循环] repeat [每次执行的操作])✅
D. (loop [设置循环] for [每次执行的操作])✅
知识点: Loop的For样式循环
正确答案: D
解析: Loop中For样式循环的正确用法包括:from…to、in、across等。选项D中的”between…and”不是Loop中的标准语法。
速记提示: 记住常用的”from to”、”in”和”across”,排除不常见的表达。
题目: 以下哪个不是Loop中For样式循环的正确用法?
选项:
A. for N from 1 to 5✅
B. for N in ‘(1 2 3 4 5)✅
C. for N across #(1 2 3 4 5)✅
D. for N between 1 and 5✅
知识点: Loop的While/Until样式循环
正确答案: A
解析: Loop中While样式循环的正确用法是:for N = X then Z while (condition) do (body)。这里,X是初始值,Z是每次循环的递增值,while后面跟随循环继续的条件。
速记提示: 记住”for…then…while”的结构,表示”从…然后…当…时”。
题目: 以下哪个是Loop中While样式循环的正确用法?
选项:
A. for N = X then Z while (condition) do (body)✅
B. for N = X then Z until (condition) do (body)✅
C. while N = X then Z do (body)✅
D. until N = X then Z do (body)✅
知识点: Loop的Repeat用法
正确答案: A
解析: 使用Loop的repeat关键字重复执行操作的正确方式是:(loop repeat 5 do (print “Hello”))。这将打印”Hello”5次。
速记提示: “repeat”直接跟数字,简单明了。
题目: 如何使用Loop的repeat关键字重复执行5次操作?
选项:
A. (loop repeat 5 do (print “Hello”))✅
B. (loop 5 times do (print “Hello”))✅
C. (loop for i in 1 to 5 do (print “Hello”))✅
D. (loop while i < 5 do (print “Hello”))✅
知识点: Loop的条件语句
正确答案: C
解析: Loop中的条件语句包括if、when和do…until。”unless”不是Loop中的标准条件语句。
速记提示: 记住常用的”if”、”when”和”until”,排除不常见的”unless”。
题目: 以下哪个不是Loop中的条件语句?
选项:
A. if (condition) do (body) else do (body) end✅
B. when (condition) do (body) end✅
C. unless (condition) do (body) end✅
D. do (body) until (condition) end✅
知识点: Loop的真/假条件
正确答案: C
解析: “thereis”关键字用于检查循环中是否存在满足条件的情况。如果存在,则返回true。
速记提示: “thereis”可以理解为”有没有”,询问是否存在。
题目: 哪个Loop关键字用于检查循环中是否存在满足条件的情况?
选项:
A. always✅
B. never✅
C. thereis✅
D. sometimes✅
知识点: Loop的Initially/Finally
正确答案: C
解析: 使用”initially”关键字可以在Loop循环开始前执行一些代码。例如:(loop initially (print “start”) for y from 1 to 5 do (print y))
速记提示: “initially”意为”最初”,正好对应循环开始前。
题目: 如何在Loop循环开始前执行一些代码?
选项:
A. before (body)✅
B. start (body)✅
C. initially (body)✅
D. begin (body)✅
知识点: Loop的解构
正确答案: C
解析: Loop中正确的解构绑定用法是使用”in”关键字,如:(loop for (a b) in ‘((1 2) (3 4) (5 6)) do (print (list a b)))。这样可以将每个子列表的元素分别绑定到a和b。
速记提示: 记住”in”是用于遍历列表的关键字。
题目: 以下哪个是Loop中正确的解构绑定用法?
选项:
A. (loop for (a b) of ‘((1 2) (3 4) (5 6)) do (print (list a b)))✅
B. (loop for (a b) from ‘((1 2) (3 4) (5 6)) do (print (list a b)))✅
C. (loop for (a b) in ‘((1 2) (3 4) (5 6)) do (print (list a b)))✅
D. (loop for (a b) = ‘((1 2) (3 4) (5 6)) do (print (list a b)))✅
知识点: Loop的收集关键字
正确答案: D
解析: Loop中的数据收集关键字包括collect、append、nconc、count、sum、maximize和minimize。”gather”不是标准的Loop收集关键字。
速记提示: 记住常用的”collect”、”append”和”nconc”,排除不常见的”gather”。
题目: 以下哪个不是Loop中的数据收集关键字?
选项:
A. collect✅
B. append✅
C. nconc✅
D. gather✅
知识点: Loop的变量声明
正确答案: C
解析: 在Loop中使用”with”关键字可以声明一个新的循环变量。例如:(loop with x = 0 for y from 1 to 5 do (setf x (+ x y)))
速记提示: “with”在英语中表示”带有”,这里表示循环带有一个新变量。
题目: 如何在Loop中声明一个新的循环变量?
选项:
A. declare new-loop-var✅
B. let new-loop-var✅
C. with new-loop-var✅
D. var new-loop-var✅
知识点: Format函数的基本语法
正确答案: A
解析: Format函数的基本语法是(format stream control-string data1 data2…)。其中stream可以是t(标准输出)、nil(返回字符串)或一个流对象。
速记提示: 记住顺序:首先指定输出位置(stream),然后是控制字符串,最后是数据。
题目: Format函数的基本语法是什么?
选项:
A. (format stream control-string data1 data2…)✅
B. (format control-string stream data1 data2…)✅
C. (format data1 data2… control-string stream)✅
D. (format data1 data2… stream control-string)✅
知识点: Format的流参数
正确答案: B
解析: 在Format函数中,如果将stream参数设置为nil,函数将返回格式化后的字符串,而不是直接输出到标准输出流。
速记提示: nil表示”无”,这里表示不输出到任何流,而是返回字符串。
题目: 在Format函数中,如果想要返回格式化后的字符串而不是直接输出,stream参数应该设置为什么?
选项:
A. t✅
B. nil✅
C. string✅
D. return✅
知识点: Format的基本指令
正确答案: C
解析: 在Format函数中,~%指令用于插入换行符。例如:(format t “Hello~%World”) 将输出两行文本。
速记提示: %符号在很多编程语言中都用于表示特殊字符,这里用于换行。
题目: 在Format函数中,哪个指令用于插入换行符?
选项:
A. ~N✅
B. ~L✅
C. ~%✅
D. ~R✅
知识点: Format的~A指令
正确答案: C
解析: Format函数中的~A指令用于将任何Lisp类型转换为其打印表示。它是一个通用的参数转换器。
速记提示: A可以理解为”Any”,表示可以处理任何类型。
题目: Format函数中的~A指令的作用是什么?
选项:
A. 将参数转换为ASCII码✅
B. 将参数转换为数组✅
C. 将任何Lisp类型转换为其打印表示✅
D. 将参数转换为地址✅
知识点: Format的列表处理
正确答案: C
解析: 在Format函数中,使用~{…~}指令可以处理列表中的每个元素。例如:(format t “~{Element: ~A~%~}” ‘(1 2 3 4))
速记提示: 花括号{}在很多语言中用于表示代码块或集合,这里用于处理列表集合。
题目: 在Format函数中,如何处理列表中的每个元素?
选项:
A. 使用~L…~L指令✅
B. 使用~E…~E指令✅
C. 使用~{…~}指令✅
D. 使用~[…~]指令✅
知识点: Format的数值格式化
正确答案: B
解析: 在Format函数中,~F指令用于以固定小数位数格式化浮点数。例如:(format nil “~,2F” 3.14159) 将输出”3.14″。
速记提示: F可以理解为”Fixed”,表示固定小数位数。
题目: 在Format函数中,哪个指令用于以固定小数位数格式化浮点数?
选项:
A. ~D✅
B. ~F✅
C. ~E✅
D. ~G✅
知识点: Format的条件指令
正确答案: B
解析: 在Format函数中,~[…~]指令用于根据参数值选择不同的输出格式。例如:(format nil “~[零~;一~;二~:;很多~]” 2) 将输出”二”。
速记提示: 方括号[]常用于表示选择或索引,这里用于根据索引选择输出。
题目: Format函数中,哪个指令用于根据参数值选择不同的输出格式?
选项:
A. ~{…~}✅
B. ~[…~]✅
C. ~(…)✅
D. ~<…~>✅
知识点: Format的递归处理
正确答案: A
解析: 在Format函数中,可以使用嵌套的~{…~}指令来递归处理嵌套的列表结构。例如:(format nil “~{(~{~A~^ ~})~^ ~}” ‘((1 2 3) (4 5 6)))
速记提示: 嵌套的花括号表示嵌套的列表处理。
题目: 在Format函数中,如何递归处理嵌套的列表结构?
选项:
A. 使用~{~{…~}~}✅
B. 使用~[~[…~]~]✅
C. 使用~(~(…~)~)✅
D. 使用~<~<…~>~>✅
知识点: Format的大小写转换
解析: 在Format函数中,~:@(指令用于将输出转换为大写。例如:(format nil “~:@(hello, world~)”) 将输出”HELLO, WORLD”。
速记提示: @符号常用于表示特殊操作,这里用于大写转换。
- Common Lisp – The Tutorial Part 8.pdf
- http://www.lispworks.com/documentation/lw50/CLHS/Body/22_ck.htm
- https://www.hexstreamsoft.com/articles/common-lisp-format-reference/clhs-summary/
题目: 在Format函数中,哪个指令用于将输出转换为大写?
选项:
A. ~U✅
B. ~C✅
C. ~:@(✅
D. ~S✅
正确答题: C
总结
本学习材料涵盖了Common Lisp中Loop和Format的基本概念和用法。Loop宏是一个强大的迭代工具,提供了多种循环方式,包括For样式、While/Until样式、条件循环等。它还支持数据收集、变量声明和解构绑定等高级特性。Format函数是Lisp中通用的数据到字符串的转换工具,提供了丰富的指令来控制输出格式,包括基本的字符串插入、数值格式化、列表处理和条件输出等。掌握这些工具将大大提高你的Lisp编程效率和代码可读性。
参考文献