2016-10-05 4 views
1

Я пытался понять, как использовать циклы в LISP, и они по-прежнему не работают корректно. Я пробовал использовать следующий код:Как использовать циклы в 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

Удалите скобки '(если (эк ...) (return-from ...)) 'so to have' when (eq ...) (return-from ...) 'и помещать' do' before' (return-from ...) '. – Renzo

+4

[Практический общий Лисп] (http://www.gigamonkeys.com/book/) имеет главу о петле под названием [LOOP для черных поясов] (http://www.gigamonkeys.com/book/loop-for-black -belts.html). В книге [Страна lisp] (http://landoflisp.com/) у вас есть [практический чит-лист] (http://blog.idorobots.org/media/lolreview/3.png) – Sylwester

ответ

4

Основная проблема в цикле - это выражение WHEN. Есть два способа вы можете написать, что:

  1. Используйте цикл WHEN condition DO forms -clause:

    (loop for... 
         when (eq ...) do (return-from ...)) 
    
  2. Использование регулярных WHEN -macro внутри цикла DO -clause:

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

Есть еще несколько вещей, которые нужно исправить в вашем коде.

  1. Называя вещи в Лиспе, используйте дефис между словами, а не верблюжьего (check-zero, а не checkZero).
  2. Используйте = для общего числового сравнения, или ZEROP, чтобы проверить, что число равно нулю. EQ используется для проверки того, являются ли два объекта одним и тем же объектом.
  3. Вы можете вернуться из цикла с помощью RETURN
  4. Я не совсем уверен, что вы пытаетесь достичь с (VALUES-LIST (NTH ... (CAR ...))), но это не будет работать. Если вы пытаетесь просто перебрать плоский список значений (например, (1 2 3 4 5 6)), вы должны использовать цикл FOR item IN list -clause.

Итак, теперь вы должны иметь что-то вроде:

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

LOOP также имеет THEREIS condition -clause, что вы могли бы использовать:

(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) 

Это возвращает обобщенное логическое значение. То есть любое значение, которое не равно NIL, считается истинным в Common Lisp.

Поскольку существует функция предиката (ZEROP), чтобы проверить, если объект является нулевым, вы можете также использовать SOME или MEMBER-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) 
+0

Небольшое предупреждение, если список для проверки может законно содержать не номера, используйте 'equalp' (если вы хотите сравнить 0 и 0.0) или' eql' (если вы хотите, чтобы и числовое значение, и числовой тип были одинаковыми), так как 'zerop 'должен сигнализировать об ошибке, если аргумент является не числом, а' = '- вероятным. – Vatine

Смежные вопросы