关于用 lisp 替换 while 和 if 语句的问题



我想将下面的代码更改为Lisp代码,但是语法上不断出现错误。如何修复 if 语句?

int promising(int i)  
{
int k = 1;
while (k < i)
{
if (col[i] == col[k] || abs(col[i] - col[k]) == abs(i - k))  
return 0;
k++;
}
return 1;
}

下面是我改成Lisp代码的那个。

(setq col (list 0 0 0 0))
(DEFUN promising (i)
(let ((k 1)) ; k =1
(loop while (< k i) 
do((if (or ( = (nth i col) (nth k col)) 
(= ( abs((setq a (- (nth i col) (nth k col)))))
( abs((setq b (- i k ))))))
(return-from promising 0)))
do (setq k (1+ k)))
(return-from promising 1))
)

我很难灵活地将 if 语句的复杂条件更改为 lisp 代码。

你正在做"C-in-Lisp"。试图直接C(或者就此而言,C++/Java/C#/Python......)程序翻译成Lisp通常会导致糟糕的代码,你最好试着理解这些问题是如何在Lisp中解决的。

话虽如此:

  • 不应在顶层使用(setq col <whatever>)。全局变量是使用defvardefparameter引入的,它们的名称形式*variable-name*以区别于其他变量。这是因为它们具有不同的范围规则,并且行为不同(它们不等同于其他语言的全局变量)。特别是,将setq与尚未声明defvardefparameter的变量一起使用是未定义的行为,大多数实现都允许这样做,但它们随后会创建此全局变量。你通常不希望这样。总结一下:如果需要,要么使用(defvar *col* (list 0 0 ...))这确实是一个全局变量,要么只是在你需要的地方使用(let ((col (list 0 ...))) <more-code>)

  • loop是一个复杂的结构。这本身就是另一种你必须在Lisp之上学习的迷你语言。特别是,如果你只想"在某些边界之间循环一些变量,并在每一步增加一些值",请使用

    (从 1 开始循环 k do ...) ;; 这引入了一个局部变量 k,每一步递增 1,没有上限 (从 1 到 10 的循环 k 做 ...) ;;相同,但只循环直到 k 达到 10 (从 1 到 10 的循环 k 乘以 3 做...)相同,但每一步将 k 递增 3

其他构造也可用。阅读Practical Common Lisp的这一部分以获得良好的介绍,阅读相关的 CLHS 部分以获取技术说明和文档。

  • 请遵循空格的约定,这使得它更容易阅读。例如,永远不要将括号单独放在其行上(e.g.最后一个括号),( abs((setq b (- i k ))))真的应该写(abs ((setq b (- i k))))(忽略这是不正确的事实,见下文......就样式而言,您还需要修复缩进,并且不要写大写DEFUN,这是不必要的,看起来很奇怪。
  • 你不能放置额外的括号只是为了将事物组合在一起,括号具有语义意义。特别是,在大多数情况下,调用函数或使用几乎任何特殊运算符都是由(<operator-name> <first-arg> <second-arg> ... )完成的。您几乎从来没有 2 个连续的左括号。
  • 为什么要在循环中使用(setq a ...)(setq b ...)ab都没有在其他任何地方声明或使用。
  • 如果要访问列表的特定元素,请不要使用列表,而是使用向量。特别是,对nth函数的多次调用通常表明您确实应该使用向量。

代码的正确版本,使用一些loop工具,并且仍然假设col是一个列表(不应该是),尽管会有其他循环结构使这一点更加清晰......

(defun promising (i)
(loop for k from 1 below i
for col-k = (nth k col)
do (when (or (= (nth i col) (nth k col))
(= (abs (- (nth i col) (nth k col)))
(abs (- i k))))
(return-from promising 0)))
1)

请注意,这段代码效率非常低,这就是为什么我建议不要直接从 C 转换为 Lisp。特别是,尽管您遍历了一个列表(您在第 k 步访问第 k 个元素),但您的代码在每一步都调用nth而不是遍历列表!您还可以在每一步计算(nth i col),这在 C 中已经毫无用处(它是常量的,因此不需要在每一步都重新计算),但在这里是灾难性的。这真的应该是:

(defun promising (i)
(let ((col-i (nth i col)))
(loop for k from 1 below i
for col-k in (cdr col) ;; you start from the index 1, not 0
do (when (or (= col-i col-k)
(= (abs (- col-i col-k))
(abs (- i k))))
(return-from promising 0))))
1)

代码中有几个错误。

  1. 不正确的函数调用
(DEFUN promising (i)
(let ((k 1)) ; k =1
(loop while (< k i) 
do((if (or ( = (nth i col) (nth k col))
;;        ^^
;;        Incorrect
)

还有这里:

(setq col (list 0 0 0 0))
(DEFUN promising (i)
(let ((k 1)) ; k =1
(loop while (< k i) 
do((if (or ( = (nth i col) (nth k col)) 
(= ( abs((setq a (- (nth i col) (nth k col)))))
;;                  ^^
;;                   Incorrect
( abs((setq b (- i k ))))))
;;               ^^
;;                Incorrect
(return-from promising 0)))
do (setq k (1+ k)))
(return-from promising 1))
)
  1. loop宏有 2 个do关键字
(setq col (list 0 0 0 0))
(DEFUN promising (i)
(let ((k 1)) ; k =1
(loop while (< k i) 
do((if (or ( = (nth i col) (nth k col))
;;      ^^
;;       First do
(= ( abs((setq a (- (nth i col) (nth k col)))))
( abs((setq b (- i k ))))))
(return-from promising 0)))
do (setq k (1+ k)))
;;      ^^
;;       Second do
(return-from promising 1))
)
  1. return-from被多次使用

return-from通常不会出现在Common Lisp代码中,这很像C goto,这是开发人员试图避免的。

  1. 定义ab的不连贯setq(可能是旧代码)
(setq col (list 0 0 0 0))
(DEFUN promising (i)
(let ((k 1)) ; k =1
(loop while (< k i) 
do((if (or ( = (nth i col) (nth k col)) 
(= ( abs((setq a (- (nth i col) (nth k col)))))
;;                    ^^^^^^
;;                      ??
( abs((setq b (- i k ))))))
;;                 ^^^^^^
;;                   ??
  1. 奇怪的增量方案
(setq k (1+ k))

虽然是正确的,但Common Lisp程序员将简单地使用增量函数:

(incf k)
  1. 最终代码

您可能要查找的代码应该接近该代码:

(defun promising (i)
(let ((k 1))
(loop while (< k i) do
(if (or (= (nth i col) (nth k col))
(= (abs (- (nth i col) (nth k col)))
(abs (- i k ))))
(return-from promising 0))
(incf k))
;; return value
k))

请注意,代码不等同C版本,因为数据结构完全不同。在C版本中,对数据的访问将非常快O(1)。如果您有大量O(n)元素,Common Lisp 版本会很慢。如果你用数组/向量替换你的列表,你可以快速使用Common Lisp。

最新更新