我一直在阅读一些lisp代码,并且遇到了这一部分,并不太明白它具体是做什么的,尽管整个函数应该计算输入文本中出现的-z字母的次数。
(do ((i #.(char-code #\a) (1+ i)))
((> i #.(char-code #\z)))
任何人都可以一步一步解释发生了什么吗?我知道它以某种方式计算字母,但不太确定如何。
答案 0 :(得分:9)
这个Lisp代码有点不寻常,因为它使用了读取时间评估。 #.expr
表示在读取时间内仅对表达式进行一次计算。
在这种情况下,聪明的编译器可能已经猜到给定字符的字符代码是已知的,并且可能已经从DO
循环中删除了字符代码的计算。该代码的作者选择通过在编译器看到它之前评估表达式来做到这一点。
源代码如下:
(do ((i #.(char-code #\a) (1+ i)))
((> i #.(char-code #\z)))
...)
当Lisp读入s表达式时,我们得到这个新代码作为结果(假设通常的字符编码):
(do ((i 97 (1+ i)))
((> i 122))
...)
这是一个循环,它将变量i
从97计算到122.
答案 1 :(得分:1)
Lisp代码写为S-Expression。在典型的S表达式sytax中,任何S表达式的第一个元素被视为运算符,其余元素被视为操作数。操作数可以是原子或另一个S表达式。请注意,atom是单个数据对象。牢记这一点
(char-code #\a) - returns the ascii representation of a character here its 'a'.
do语法类似于下面的
(do ((var1 init1 step1)
(var2 init2 step2)
...)
(end-test result)
statement1
...)
所以在你的例子中
(do ((i #.(char-code #\a) (1+ i)))
((> i #.(char-code #\z)))
)
do的第一个s表达式操作数是循环初始化,第二个s表达式操作数是结束测试。 所以这意味着你只需要通过'a'迭代'z'将i递增1。
在C ++中(不确定你的其他语言舒适度,你可以写
for(i='a';i<='z';i++);
答案 2 :(得分:0)
你展示的代码的技巧很糟糕。我知道这是因为我做到了这一切 时间。代码假设编译器将知道当前的fixnum 对于每个角色。 #。(char-code#\ a)eq [(或者,如果你是如此倾斜的话,可能是eql)无符号小整数或无符号8位字符,返回值为正数fixnum]。
#是一个读者宏(我很确定你知道这个:)。使用两个读取器宏是 不是一个好主意,但是当编译器知道数据类型时它很快。
我有另一个例子。需要在二进制流中搜索ascii:
(defmacro code-char= (byte1 byte2)
(flet ((maybe-char-code (x) (if characterp x) (char-code x) x)))
`(the fixnum (= (the fixnum ,(maybe-char-code byte1)
(the fixnum ,(maybe-char-code byte2)))))
))
在sbcl中声明返回类型可能会侮辱编译器,但我将其作为一个完整性检查(4我不是你)。
(code-char =#\ $#x36) =&GT; 吨
。至少我是这么认为的。但不知怎的,我想你可能知道你的方式围绕一些宏...嗯...我应该打开机器......
如果您对此感兴趣,可以使用跳转表来安装286(8/16位dos汇编程序)的汇编程序。它适用于PC,我必须查找它...