Lisp - "when" 无论如何执行条件?
Lisp - "when" condition executing no matter what?
我正在定义一个函数来测试一个数字是否为质数,并且我有一个有效的算法(在 Python 中),我已经将其中的大部分移植到 Lisp 中。然而,问题是我的素性测试即使在不应该通过的时候也会通过。例如,isPrime(13)
仍然达到 return NIL
,即使它不满足 when
条件。
(defun isPrime(n)
(cond
((< n 2); numbers less than 2 aren't prime
NIL
)
((equal n 2); 2 is the only even prime
T
)
((equal (rem n 2) 0); Even numbers are never prime (besides 2)
NIL
)
((> n 2)
(loop for i from 2 to n do(
when(equal (rem n i) 0);If n is evenly divisible by i, we have found a factor other than 1 and n
(return NIL)
)
)
)
(t T); If we get through all that with no issue, the number is prime
)
)
问题:为什么我的函数无论如何都会到达return NIL
分支?
此外,如果这只是测试素数的一种糟糕方法,是否有更像 lisp 的方法(不担心性能,只担心算法的正确性和可读性。)
您的代码永远不会达到 t
条件。
(> n 2)
使执行进入 LOOP
。 COND
将 return LOOP 的值。
Why does my function reach the return NIL branch no matter what?
你的循环形式总是return NIL。
有关 LOOP 的详细解释,请参阅 22. LOOP for Black Belts。
不累积结果或显式return 值returns NIL 的循环形式。一个简单的修复方法是在循环后使用 return T,但有更好的表达方式:
(loop for i from 2 below n never (= (rem n i) 0))
默认条款
您写道:
If we get through all that with no issue, the number is prime
这是对的,但它仅适用于 cond
表达式中的测试。一旦守卫得到满足,比如 (> n 2)
,cond
的值将是与该测试相关的主体 return 编辑的任何值。换句话说,子句不回溯(像在 Prolog 中)或 fall-through(像在 C 开关中)。
你可以有一个子句,其中测试可以 return NIL,这将使 COND
尝试下一个子句:
If there are no forms in that clause, the primary value of the test-form is returned by the cond form.
但这可能会造成混淆。
正在格式化
你应该尽量遵守 Lisp 的约定,使用所谓的 kebab-case
(dash-separated 个单词)来命名你的符号,并且不要在左括号之后或之前留下空格右括号。
(defun is-prime (n)
(cond
...))
我个人也喜欢尽可能避免早 returns。事实上,如果可能的话,我发现在没有分支的情况下表达结果更具可读性(逻辑运算符是 short-circuiting,但这是一个细节):
(defun is-prime (n)
(and (> n 1)
(or (= n 2)
(loop
for i from 2 to (isqrt n)
never (= 0 (rem n i))))))
注意我们只需要计算 i
到 n
的平方根:
Why do we check up to the square root of a prime number to determine if it is prime?
编辑:另见 tfb 的回答(从 3 开始,逐步进行 2)
首先,您的代码有一个相当明显的错误:一旦您遇到 cond
的 (> n 2)
情况,那么它要么显式 return nil
否则它将到达循环的末尾并且...隐式 return nil
。 cond
的最终情况将永远不会达到。
这是它的一个版本
- 使用标准 Lisp 约定格式化,而不是从 C 或其他地方导入的约定;
- 为函数使用传统的 Lisp 名称;
- 使用更好的操作(例如,将数字与
=
而非 equal
进行比较);
- 在循环上使用更好的边界和更好的步骤(无需检查偶数,您已经完成了);
- 修复了错误。
(defun primep (n)
(cond
((< n 2)
;; numbers less than 2 are not prime
nil)
((= n 2)
;; 2 is prime
t)
((evenp n)
;; even numbers are not prime
nil)
(t
;; Otherwise it is a prime if no odd integer less than or equal to
;; its root divides it.
(loop for i from 3 to (isqrt n) by 2
never (zerop (rem n i))))))
然而,在 Lisp 中表达这一点的更自然的方式可能是用英语说出你会说的话:
n is prime if it is 2 or if it is greater 2 and if it is odd, and if it has no odd divisors less than or equal to its square root.
我们会这样写
(defun primep (n)
(or (= n 2) ;2 is prime ...
(and ;... otherwise ...
(> n 2) ;... primes must be > 2 ...
(oddp n) ;... odd ...
;; ... and have no odd divisorts <= their roots
(loop for i from 3 to (isqrt n) by 2
never (zerop (rem n i))))))
最后您可能想要检查参数是否具有合理的类型:素性测试对自然数有意义,因此:
(defun primep (n)
(check-type n (integer 0) "a natural number")
(or (= n 2) ;2 is prime ...
(and ;... otherwise ...
(> n 2) ;... primes must be >= 2 ...
(oddp n) ;... odd ...
;; ... and have no odd divisorts <= their roots
(loop for i from 3 to (isqrt n) by 2
never (zerop (rem n i))))))
我正在定义一个函数来测试一个数字是否为质数,并且我有一个有效的算法(在 Python 中),我已经将其中的大部分移植到 Lisp 中。然而,问题是我的素性测试即使在不应该通过的时候也会通过。例如,isPrime(13)
仍然达到 return NIL
,即使它不满足 when
条件。
(defun isPrime(n)
(cond
((< n 2); numbers less than 2 aren't prime
NIL
)
((equal n 2); 2 is the only even prime
T
)
((equal (rem n 2) 0); Even numbers are never prime (besides 2)
NIL
)
((> n 2)
(loop for i from 2 to n do(
when(equal (rem n i) 0);If n is evenly divisible by i, we have found a factor other than 1 and n
(return NIL)
)
)
)
(t T); If we get through all that with no issue, the number is prime
)
)
问题:为什么我的函数无论如何都会到达return NIL
分支?
此外,如果这只是测试素数的一种糟糕方法,是否有更像 lisp 的方法(不担心性能,只担心算法的正确性和可读性。)
您的代码永远不会达到 t
条件。
(> n 2)
使执行进入 LOOP
。 COND
将 return LOOP 的值。
Why does my function reach the return NIL branch no matter what?
你的循环形式总是return NIL。
有关 LOOP 的详细解释,请参阅 22. LOOP for Black Belts。
不累积结果或显式return 值returns NIL 的循环形式。一个简单的修复方法是在循环后使用 return T,但有更好的表达方式:
(loop for i from 2 below n never (= (rem n i) 0))
默认条款
您写道:
If we get through all that with no issue, the number is prime
这是对的,但它仅适用于 cond
表达式中的测试。一旦守卫得到满足,比如 (> n 2)
,cond
的值将是与该测试相关的主体 return 编辑的任何值。换句话说,子句不回溯(像在 Prolog 中)或 fall-through(像在 C 开关中)。
你可以有一个子句,其中测试可以 return NIL,这将使 COND
尝试下一个子句:
If there are no forms in that clause, the primary value of the test-form is returned by the cond form.
但这可能会造成混淆。
正在格式化
你应该尽量遵守 Lisp 的约定,使用所谓的 kebab-case
(dash-separated 个单词)来命名你的符号,并且不要在左括号之后或之前留下空格右括号。
(defun is-prime (n)
(cond
...))
我个人也喜欢尽可能避免早 returns。事实上,如果可能的话,我发现在没有分支的情况下表达结果更具可读性(逻辑运算符是 short-circuiting,但这是一个细节):
(defun is-prime (n)
(and (> n 1)
(or (= n 2)
(loop
for i from 2 to (isqrt n)
never (= 0 (rem n i))))))
注意我们只需要计算 i
到 n
的平方根:
Why do we check up to the square root of a prime number to determine if it is prime?
编辑:另见 tfb 的回答(从 3 开始,逐步进行 2)
首先,您的代码有一个相当明显的错误:一旦您遇到 cond
的 (> n 2)
情况,那么它要么显式 return nil
否则它将到达循环的末尾并且...隐式 return nil
。 cond
的最终情况将永远不会达到。
这是它的一个版本
- 使用标准 Lisp 约定格式化,而不是从 C 或其他地方导入的约定;
- 为函数使用传统的 Lisp 名称;
- 使用更好的操作(例如,将数字与
=
而非equal
进行比较); - 在循环上使用更好的边界和更好的步骤(无需检查偶数,您已经完成了);
- 修复了错误。
(defun primep (n)
(cond
((< n 2)
;; numbers less than 2 are not prime
nil)
((= n 2)
;; 2 is prime
t)
((evenp n)
;; even numbers are not prime
nil)
(t
;; Otherwise it is a prime if no odd integer less than or equal to
;; its root divides it.
(loop for i from 3 to (isqrt n) by 2
never (zerop (rem n i))))))
然而,在 Lisp 中表达这一点的更自然的方式可能是用英语说出你会说的话:
n is prime if it is 2 or if it is greater 2 and if it is odd, and if it has no odd divisors less than or equal to its square root.
我们会这样写
(defun primep (n)
(or (= n 2) ;2 is prime ...
(and ;... otherwise ...
(> n 2) ;... primes must be > 2 ...
(oddp n) ;... odd ...
;; ... and have no odd divisorts <= their roots
(loop for i from 3 to (isqrt n) by 2
never (zerop (rem n i))))))
最后您可能想要检查参数是否具有合理的类型:素性测试对自然数有意义,因此:
(defun primep (n)
(check-type n (integer 0) "a natural number")
(or (= n 2) ;2 is prime ...
(and ;... otherwise ...
(> n 2) ;... primes must be >= 2 ...
(oddp n) ;... odd ...
;; ... and have no odd divisorts <= their roots
(loop for i from 3 to (isqrt n) by 2
never (zerop (rem n i))))))