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

Questions about replacing while and if statements with lisp

我想将下面的代码更改为 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代码

代码中有几处错误。

  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. 不连贯 setq 定义 ab(可能是旧代码)
(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 版本会很慢。如果您用 array/vector.

替换您的列表,您可以快速使用 Common 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 之上学习的另一个 mini-language。特别是,如果您想要做的只是“在某些边界之间循环一些变量并在每一步将其递增某个值”,请使用

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

其他结构可用。阅读 this section of Practical Common Lisp for a good introduction, and the relevant CLHS section 以获得技术说明和文档。

  • 请遵循空格约定,这样更容易阅读。例如,永远不要在它的行上单独放置一个括号(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-th 步骤访问了 k-th 元素),但您的代码在每一步都调用了 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)