#loops #common-lisp
Вопрос:
Я пытался использовать стандартные средства цикла для получения результата, но он просто возвращает ноль. Почему это так? такое чувство, что это должно сработать:
(defun coll-intersects (bounds mv)
(let ((res (list))
(loop for x from (first bounds) to ( (first bounds) (third bounds)) do
(loop for y from (second bounds) to ( (second bounds) (fourth bounds))
if (not (member (cl-byte (aref mapa x y)) mv))
collect (aref mapa x y) into res
))))
но нет, я должен это сделать:
(defun coll-intersects (bounds mv)
(let ((res (list)))
(loop for x from (first bounds) to ( (first bounds) (third bounds)) do
(loop for y from (second bounds) to ( (second bounds) (fourth bounds))
do
(if (not (member (cl-byte (aref mapa x y)) mv))
(push (aref mapa x y) res))
))
res))
почему? я был действительно сбит с толку, почему первый не работал
Комментарии:
1. Это опечатка с копипастом? В
let
первом примере отсутствует закрывающая скобка:(let ((res (list)))
. Ваш шепелявый должен предупредить вас о неправильной привязке let.2. извините, это было воссоздание того, что я пробовал. В том, что я попробовал, я не пропустил эту закрывающую скобу
Ответ №1:
Как говорится в ответе Эвинса, проблема в том, что
(loop ...
collect ... into x
...)
связывает x
. Цель этой конструкции на самом деле состоит в том, чтобы вы могли собирать несколько списков:
(defun partition (l)
(loop for e in l
if (evenp e)
collect e into evens
else
collect e into odds
finally (return (values evens odds))))
например.
В случае, когда вы хотите собрать один список из вложенных циклов и вас волнует порядок, вы можете выполнить этот трюк:
(defun sublist-evens (l)
(loop for s in l
nconcing
(loop for e in s
when (evenp e)
collect e)))
Здесь внешний цикл, по существу nconc
, объединяет результаты внутреннего цикла. Это, конечно, может гнездиться:
(loop ...
nconcing
(loop ...
nconcing
(loop ...
collect ...)))
будет работать. Также возможно , что loop
это достаточно умно, чтобы держать хвостовой указатель на список, который он создает с nconc
помощью/ nconcing
, хотя вам придется это проверить.
Однако в тех случаях, когда вы хотите построить какой-либо список по порядку из какого-либо глубоко вложенного цикла (или любого другого процесса поиска) Я нахожу, что почти всегда приятнее использовать для этого макрос сбора (отказ от ответственности: я написал это). С таким макросом вышеуказанная sublist-evens
функция выглядит следующим образом:
(defun sublist-evens (l)
(collecting
(dolist (s l)
(dolist (e s)
(when (evenp e) (collect e))))))
и
> (sublist-evens '((1 2 3) (4 5 6)))
(2 4 6)
И вы можете сделать лучше:
(defun tree-partition (tree)
(with-collectors (evens odds)
(labels ((search (it)
(typecase it
(list
(dolist (e it)
(search e)))
(integer
(if (evenp it)
(evens it)
(odds it)))
(t
(warn "unexpected ~A" (type-of it))))))
(search tree))))
а теперь
> (tree-partition '(((1 2 3) (4)) 5))
(2 4)
(1 3 5)
(И для значения взлома вы можете использовать другой макрос, чтобы выразить вышесказанное более кратко:
(defun tree-partition (tree)
(with-collectors (evens odds)
(iterate search ((it tree))
(typecase it
(list
(dolist (e it)
(search e)))
(integer
(if (evenp it)
(evens it)
(odds it)))
(t
(warn "unexpected ~A" (type-of it)))))))
Отказ от ответственности: я тоже написал этот макрос.)
Комментарии:
1. согласна, милая! Я также укажу на сбор коммунальных услуг в Серапеуме: github.com/ruricolist/serapeum/blob/master/…
2. @Ehvince: да, я думаю, что поколения изобретали такие вещи, как
collecting
. Мой пришел от Interlisp-DTCONC
или, возможноDOCOLLECT
, /ENDCOLLECT
пары: я не уверен, какой именно, но я знаю, что написал оригинал на D-машине.
Ответ №2:
Вот первый фрагмент, с let
исправленными скобками, упрощенный, чтобы его можно было воспроизводить:
(defun coll-intersects (bounds mv)
(let ((res (list))) ;; <-- third closing paren
(loop for x from (first bounds) to ( (first bounds) (third bounds)) do
(loop for y from (second bounds) to ( (second bounds) (fourth bounds))
if (evenp y)
collect y into res
))))
Теперь, когда я ввожу его в REPL, SBCL предупреждает меня о неиспользованном res
:
; caught STYLE-WARNING:
; The variable RES is defined but never used.
Это большой намек.
Проблемы, которые я вижу:
- вы используете
do
для внешнего цикла, а не собираете, и вы не возвращаетеres
, поэтому функции всегда возвращают ноль. collect … into
предположительно использует внутренние переменные, а не вашиres
:S. Кроме того, цикл не говорит, что с ним делать. Я добавилfinally (return res)
и получаю результаты. Вы также можете использоватьpush
, как во втором примере. Но это не кажется необходимым использоватьinto
, просто используйтеcollect y
.- обычно нет необходимости объявлять промежуточные переменные с помощью внешнего
let
.
Вот более простая функция, которая возвращает (тупые) результаты:
(defun coll-intersects (bounds)
(loop for x from (first bounds) to ( (first bounds) (third bounds)) collect
(loop for y from (second bounds) to ( (second bounds) (fourth bounds))
if (evenp y)
collect y)))
(coll-intersects '(1 2 3 4))
((2 4 6) (2 4 6) (2 4 6) (2 4 6))
Если вы используете nconcing
вместо первого collect
, вы получите плоский список (как указано @tfb).
или:
(defun coll-intersects (bounds)
(let ((res (list)))
(loop for x from (first bounds) to ( (first bounds) (third bounds)) do
(loop for y from (second bounds) to ( (second bounds) (fourth bounds))
if (evenp y)
do (push y res)
))
res))
(coll-intersects '(1 2 3 4))
(6 4 2 6 4 2 6 4 2 6 4 2)
Ответ №3:
В вашем первом примере возвращаемое значение функции является возвращаемым значением из внешнего цикла. Он не собирает никаких значений (это делает внутренний цикл) и, таким образом, скорее всего, просто возвращает a nil
.
Во втором примере ваша функция явно возвращает значение res
.