Lisp - 标志(bandera)不起作用
Lisp - Flag(bandera) don't funtion
我正在尝试编写一个函数来确定一个单词是否为回文。我做了这个,但它总是 returns "Is not a palindrome"。我不知道发生了什么。
(defun palindromo (X)
(setq i 0)
(setq j (- (length X) 1))
(setq bandera 0)
(loop while (< j i)
do
(when (char= (char X i) (char X j))
(+ i 1)
(- j 1)
(setq bandera 1))
(unless (char/= (char X i) (char X j))
(setq bandera 0)
)
)
(cond
((equal 0 bandera) (write "Is not a palindrome"))
((equal 1 bandera) (write "Is a palindrome"))
)
)
我该如何解决这个问题?
循环问题
你的循环终止测试是while (< j i)
,但是你之前设置了i
和j
分别为第一个和最后一个字符的索引。这意味着 (<= i j)
。您永远不会执行循环体,并且 bandera
永远不会从其初始值 0.
进行修改
死循环问题
但是假设你修正了你的测试,让它变成 (< i j)
,那么你的循环就变成了一个无限循环,因为你 never 既不改变 i
也不改变j
在循环体中。 (+ i 1)
和 (- j 1)
这两个表达式只计算下一个索引,但不更改现有绑定。您必须像上面那样使用 setq
。
无效使用 SETQ
顺便说一句,你不能用 setq
引入变量:当试图设置一个未定义的变量时会发生什么是未定义的。您可以使用 defvar
、defparameter
引入全局变量,并使用 let
、let*
和循环关键字 with
.[=45 等引入局部变量。 =]
我假设您的 Common Lisp 实现在您执行或编译 (setq i 0)
和其他赋值时隐式定义了全局变量。但这远非理想,因为现在您的功能取决于全局状态并且不可重入。如果您从不同的线程调用 palindromo
,所有全局变量都将同时修改,这会产生不正确的结果。最好使用局部变量。
布尔逻辑
不要使用 0
和 1
作为你的标志,Lisp 使用 nil
作为布尔运算符的 false,其他一切都为 true。
混淆测试
在循环体内,你先写:
(when (char= (char X i) (char X j)) ...)
然后你写:
(unless (char/= (char X i) (char X j)) ...)
两者测试相同的东西,第二个涉及双重否定(除非不等于),这很难阅读。
风格
您通常不想从实用函数中打印东西。
您应该只 return 布尔结果。
X
这个名字有点不清楚,我会用string
。
尝试使用传统的方式格式化您的 Lisp 代码。使用自动缩进代码的编辑器(例如 Emacs)会有所帮助。另外,不要在自己的行上留下悬挂的括号。
重写
(defun palindromep (string)
(loop with max = (1- (length string))
for i from 0
for j downfrom max
while (< i j)
always (char= (char string i)
(char string j))))
- 我按照惯例在
palindrome
中添加了一个p
,因为它是一个谓词。
- 循环中的
with max = ...
定义了一个循环变量,它保存最后一个字符的索引(如果string
为空则为-1)。
i
是一个循环变量,从0 开始递增
j
是一个循环变量,从max 开始递减
while
是终止测试
always
在每次循环执行时评估一个表单,并检查它是否始终为真(非零)。
实际上,不需要外部定义的循环来判断一个字符串是否为回文。 【备注:嗯,一开始我也是这么想的。但正如@coredump 和@jkiiski 指出的那样,reverse
函数会减慢该过程,因为它将整个字符串复制一次。 ]
使用:
(defun palindromep (s)
(string= s (reverse s)))
[这个函数会比你的代码更有效率
and it returns T
if s is palindromic, else NIL
.] (不正确,它只是节省了你的写作工作量,但它比使用 loop
的过程效率低.)
详细版本为:
(defun palindromep (s)
(let ((result (string= s (reverse s))))
(write (if result
"Is a palindrome"
"Is not a palindrome"))
result))
写下你想要的答案但是 returns T
或 NIL
.
返回 T
或 NIL
的测试函数的命名约定是 'predicate' 的名称以 p
结尾。
反向函数的性能不如@coredump
建议的 while 循环
这是我新手试的速度[不推荐]:
;; Improved loop version by @coredump:
(defun palindromep-loop (string)
(loop with max = (1- (length string))
for i from 0
for j downfrom max
while (< i j)
always (char= (char string i)
(char string j))))
;; the solution with reverse
(defun palindromep (s)
(string= s (reverse s)))
;; the test functions test over and over the same string "abcdefggfedcba"
;; 10000 times over and over again
;; I did the repeats so that the measuring comes at least to the milliseconds
;; range ... (but it was too few repeats still. See below.)
(defun test-palindrome-loop ()
(loop repeat 10000
do (palindromep-loop "abcdefggfedcba")))
(time (test-palindrome-loop))
(defun test-palindrome-p ()
(loop repeat 10000
do (palindromep "abcdefggfedcba")))
(time (test-palindrome-p))
;; run on SBCL
[55]> (time (test-palindrome-loop))
Real time: 0.152438 sec.
Run time: 0.152 sec.
Space: 0 Bytes
NIL
[56]> (time (test-palindrome-p))
Real time: 0.019284 sec.
Run time: 0.02 sec.
Space: 240000 Bytes
NIL
;; note: this is the worst case that the string is a palindrome
;; for `palindrome-p` it would break much earlier when a string is
;; not a palindrome!
这是@coredump 测试函数速度的尝试:
(lisp-implementation-type)
"SBCL"
(lisp-implementation-version)
"1.4.0.75.release.1710-6a36da1"
(machine-type)
"X86-64"
(defun palindromep-loop (string)
(loop with max = (1- (length string))
for i from 0
for j downfrom max
while (< i j)
always (char= (char string i)
(char string j))))
(defun palindromep (s)
(string= s (reverse s)))
(defun test-palindrome-loop (s)
(sb-ext:gc :full t)
(time
(loop repeat 10000000
do (palindromep-loop s))))
(defun test-palindrome-p (s)
(sb-ext:gc :full t)
(time
(loop repeat 10000000
do (palindromep s))))
(defun rand-char ()
(code-char
(+ #.(char-code #\a)
(random #.(- (char-code #\z) (char-code #\a))))))
(defun generate-palindrome (n &optional oddp)
(let ((left (coerce (loop repeat n collect (rand-char)) 'string)))
(concatenate 'string
left
(and oddp (list (rand-char)))
(reverse left))))
(let ((s (generate-palindrome 20)))
(test-palindrome-p s)
(test-palindrome-loop s))
Evaluation took:
4.093 seconds of real time
4.100000 seconds of total run time (4.068000 user, 0.032000 system)
[ Run times consist of 0.124 seconds GC time, and 3.976 seconds non-GC time. ]
100.17% CPU
9,800,692,770 processor cycles
1,919,983,328 bytes consed
Evaluation took:
2.353 seconds of real time
2.352000 seconds of total run time (2.352000 user, 0.000000 system)
99.96% CPU
5,633,385,408 processor cycles
0 bytes consed
我从中学到了什么:
- 更严格地测试,根据需要重复(秒范围)
- 随机生成然后并行测试
非常感谢@coredump 的好例子!对于@jkiiski 的评论!
我正在尝试编写一个函数来确定一个单词是否为回文。我做了这个,但它总是 returns "Is not a palindrome"。我不知道发生了什么。
(defun palindromo (X)
(setq i 0)
(setq j (- (length X) 1))
(setq bandera 0)
(loop while (< j i)
do
(when (char= (char X i) (char X j))
(+ i 1)
(- j 1)
(setq bandera 1))
(unless (char/= (char X i) (char X j))
(setq bandera 0)
)
)
(cond
((equal 0 bandera) (write "Is not a palindrome"))
((equal 1 bandera) (write "Is a palindrome"))
)
)
我该如何解决这个问题?
循环问题
你的循环终止测试是while (< j i)
,但是你之前设置了i
和j
分别为第一个和最后一个字符的索引。这意味着 (<= i j)
。您永远不会执行循环体,并且 bandera
永远不会从其初始值 0.
死循环问题
但是假设你修正了你的测试,让它变成 (< i j)
,那么你的循环就变成了一个无限循环,因为你 never 既不改变 i
也不改变j
在循环体中。 (+ i 1)
和 (- j 1)
这两个表达式只计算下一个索引,但不更改现有绑定。您必须像上面那样使用 setq
。
无效使用 SETQ
顺便说一句,你不能用 setq
引入变量:当试图设置一个未定义的变量时会发生什么是未定义的。您可以使用 defvar
、defparameter
引入全局变量,并使用 let
、let*
和循环关键字 with
.[=45 等引入局部变量。 =]
我假设您的 Common Lisp 实现在您执行或编译 (setq i 0)
和其他赋值时隐式定义了全局变量。但这远非理想,因为现在您的功能取决于全局状态并且不可重入。如果您从不同的线程调用 palindromo
,所有全局变量都将同时修改,这会产生不正确的结果。最好使用局部变量。
布尔逻辑
不要使用 0
和 1
作为你的标志,Lisp 使用 nil
作为布尔运算符的 false,其他一切都为 true。
混淆测试
在循环体内,你先写:
(when (char= (char X i) (char X j)) ...)
然后你写:
(unless (char/= (char X i) (char X j)) ...)
两者测试相同的东西,第二个涉及双重否定(除非不等于),这很难阅读。
风格
您通常不想从实用函数中打印东西。 您应该只 return 布尔结果。
X
这个名字有点不清楚,我会用string
。尝试使用传统的方式格式化您的 Lisp 代码。使用自动缩进代码的编辑器(例如 Emacs)会有所帮助。另外,不要在自己的行上留下悬挂的括号。
重写
(defun palindromep (string)
(loop with max = (1- (length string))
for i from 0
for j downfrom max
while (< i j)
always (char= (char string i)
(char string j))))
- 我按照惯例在
palindrome
中添加了一个p
,因为它是一个谓词。 - 循环中的
with max = ...
定义了一个循环变量,它保存最后一个字符的索引(如果string
为空则为-1)。 i
是一个循环变量,从0 开始递增
j
是一个循环变量,从max 开始递减
while
是终止测试always
在每次循环执行时评估一个表单,并检查它是否始终为真(非零)。
实际上,不需要外部定义的循环来判断一个字符串是否为回文。 【备注:嗯,一开始我也是这么想的。但正如@coredump 和@jkiiski 指出的那样,reverse
函数会减慢该过程,因为它将整个字符串复制一次。 ]
使用:
(defun palindromep (s)
(string= s (reverse s)))
[这个函数会比你的代码更有效率
and it returns T
if s is palindromic, else NIL
.] (不正确,它只是节省了你的写作工作量,但它比使用 loop
的过程效率低.)
详细版本为:
(defun palindromep (s)
(let ((result (string= s (reverse s))))
(write (if result
"Is a palindrome"
"Is not a palindrome"))
result))
写下你想要的答案但是 returns T
或 NIL
.
返回 T
或 NIL
的测试函数的命名约定是 'predicate' 的名称以 p
结尾。
反向函数的性能不如@coredump
建议的 while 循环这是我新手试的速度[不推荐]:
;; Improved loop version by @coredump:
(defun palindromep-loop (string)
(loop with max = (1- (length string))
for i from 0
for j downfrom max
while (< i j)
always (char= (char string i)
(char string j))))
;; the solution with reverse
(defun palindromep (s)
(string= s (reverse s)))
;; the test functions test over and over the same string "abcdefggfedcba"
;; 10000 times over and over again
;; I did the repeats so that the measuring comes at least to the milliseconds
;; range ... (but it was too few repeats still. See below.)
(defun test-palindrome-loop ()
(loop repeat 10000
do (palindromep-loop "abcdefggfedcba")))
(time (test-palindrome-loop))
(defun test-palindrome-p ()
(loop repeat 10000
do (palindromep "abcdefggfedcba")))
(time (test-palindrome-p))
;; run on SBCL
[55]> (time (test-palindrome-loop))
Real time: 0.152438 sec.
Run time: 0.152 sec.
Space: 0 Bytes
NIL
[56]> (time (test-palindrome-p))
Real time: 0.019284 sec.
Run time: 0.02 sec.
Space: 240000 Bytes
NIL
;; note: this is the worst case that the string is a palindrome
;; for `palindrome-p` it would break much earlier when a string is
;; not a palindrome!
这是@coredump 测试函数速度的尝试:
(lisp-implementation-type)
"SBCL"
(lisp-implementation-version)
"1.4.0.75.release.1710-6a36da1"
(machine-type)
"X86-64"
(defun palindromep-loop (string)
(loop with max = (1- (length string))
for i from 0
for j downfrom max
while (< i j)
always (char= (char string i)
(char string j))))
(defun palindromep (s)
(string= s (reverse s)))
(defun test-palindrome-loop (s)
(sb-ext:gc :full t)
(time
(loop repeat 10000000
do (palindromep-loop s))))
(defun test-palindrome-p (s)
(sb-ext:gc :full t)
(time
(loop repeat 10000000
do (palindromep s))))
(defun rand-char ()
(code-char
(+ #.(char-code #\a)
(random #.(- (char-code #\z) (char-code #\a))))))
(defun generate-palindrome (n &optional oddp)
(let ((left (coerce (loop repeat n collect (rand-char)) 'string)))
(concatenate 'string
left
(and oddp (list (rand-char)))
(reverse left))))
(let ((s (generate-palindrome 20)))
(test-palindrome-p s)
(test-palindrome-loop s))
Evaluation took:
4.093 seconds of real time
4.100000 seconds of total run time (4.068000 user, 0.032000 system)
[ Run times consist of 0.124 seconds GC time, and 3.976 seconds non-GC time. ]
100.17% CPU
9,800,692,770 processor cycles
1,919,983,328 bytes consed
Evaluation took:
2.353 seconds of real time
2.352000 seconds of total run time (2.352000 user, 0.000000 system)
99.96% CPU
5,633,385,408 processor cycles
0 bytes consed
我从中学到了什么: - 更严格地测试,根据需要重复(秒范围) - 随机生成然后并行测试
非常感谢@coredump 的好例子!对于@jkiiski 的评论!