如何在LISP中使用循环

3

我一直在尝试理解如何在LISP中使用循环,但它们似乎仍然不能正常工作。我尝试使用以下代码:

    (loop for i from 0 to (list-length y)
          (when (eq (values-list (nth i (car y))) 0)
             (return-from checkZero t)))

需要循环遍历列表,检查值是否等于0。如果相等,则应从循环中返回并退出,否则应一直运行直到达到列表长度。我想错了吗?如果错了,我该如何修复这个循环呢?

(我不确定我的实际代码是否有效,因为我仍在处理由错误使用的循环生成的错误,而且我找不到许多在线循环使用资源)


1
移除 (when (eq ...) (return-from...)) 周围的括号,变为 when (eq ...) (return-from...) 并在 (return-from...) 前添加一个 do - Renzo
4
《实用通用Lisp》一书有一个名为“黑带循环”的章节,涉及到Lisp的循环机制。在《Lisp之国》一书中,你可以找到一个实用的速查表。 - Sylwester
1个回答

5
在循环中的主要问题是WHEN表达式。你可以用两种方式来编写它:
  1. Use the loop WHEN condition DO forms-clause:

    (loop for...
          when (eq ...) do (return-from ...))
    
  2. Use the regular WHEN-macro inside a loop DO-clause:

    (loop for...
          do (when (eq ...)
               (return-from ...)))
    

你的代码还有几个需要修复的问题。

  1. Lisp中,命名时应该使用破折号而不是驼峰式命名(例如使用 check-zero 而不是 checkZero)。
  2. 一般数值比较应该使用 =,检查数字是否为零应该使用 ZEROP。使用 EQ 是用于检查两个对象是否相同。
  3. 可以使用 RETURN 来退出循环。
  4. 我不太确定你想要使用 (VALUES-LIST (NTH ... (CAR ...))) 实现什么功能,但是它不会起作用。如果你只是想循环遍历一个平面列表(例如 (1 2 3 4 5 6)),你应该使用 loop 的 FOR item IN list 语句。

所以你现在应该有类似这样的代码:

(defun check-zero (list)
  (loop for item in list
        when (zerop item) do (return t)))

LOOP 还有一个你可以使用的 THEREIS 条件 子句:

(defun check-zero (list)
  (loop for item in list
        thereis (zerop item)))

只要找到符合 ZEROP 的项目,就返回该项目。然而,有更简单的方法来实现相同的功能。你可以使用 MEMBER 来检查列表是否包含零:

(defun check-zero (list)
  (member 0 list :test #'=))

CL-USER> (check-zero '(1 3 4 3 5 7))
NIL
CL-USER> (check-zero '(1 3 4 3 0 5 7))
(0 5 7)

这返回一个通用的布尔值。也就是说,在Common Lisp中,任何不是NIL的值都被视为true。

由于有一个谓词函数(ZEROP)可以检查一个对象是否为零,因此您还可以使用SOMEMEMBER-IF来实现:

(some #'zerop '(1 3 4 6 2 0 45 6 7)) ;=> T
(member-if #'zerop '(1 3 4 6 2 0 45 6 7)) ;=> (0 45 6 7)

小提示,如果要检查的列表可能包含非数字,请使用 equalp(如果您希望0和0.0比较相等)或 eql(如果您希望数值和数值类型都相同),因为如果参数是非数字,则zerop应该发出错误信号,而=则很可能会。 - Vatine

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接