Правильно ли использовать идиому возврата / запятой внутри (цикла ...)?

StackOverflow https://stackoverflow.com/questions/101487

  •  01-07-2019
  •  | 
  •  

Вопрос

У меня есть некоторый код, который собирает точки (согласованные целые числа) из цикла, который выглядит примерно так:

(loop
    for x from 1   to     100
    for y from 100 downto 1
        collect `(,x . ,y))

Мой вопрос в том, правильно ли использовать `(,x . ,y) в такой ситуации?

Редактировать:Этот пример не посвящен созданию таблицы из 100x100 элементов, приведенный здесь код просто иллюстрирует использование двух переменных цикла и использование их значений.Я отредактировал цикл, чтобы прояснить это.Фактический цикл, который я использую, зависит от нескольких других функций (и сам является частью одной из них), поэтому имело больше смысла заменить вызовы литеральными целыми числами и вытащить цикл из функции.

Это было полезно?

Решение

Было бы намного "лучше" просто сделать (минусы x y).

Но для того , чтобы отвечай на вопрос, в этом нет ничего плохого :) (за исключением того, что это делается немного медленнее).

Другие советы

Я думаю, что ответом здесь является использование ресурсов (следуя из Этот пост)

например , в clisp:

[1]> (time
         (progn
             (loop
                 for x from 1 to 100000
                 for y from 1 to 100000 do
                     collect (cons x y))
         ()))
WARNING: LOOP: missing forms after DO: permitted by CLtL2, forbidden by ANSI
         CL.
Real time: 0.469 sec.
Run time: 0.468 sec.
Space: 1609084 Bytes
GC: 1, GC time: 0.015 sec.
NIL
[2]> (time
         (progn
             (loop
                 for x from 1 to 100000
                 for y from 1 to 100000 do
                     collect `(,x . ,y)) ;`
         ()))
WARNING: LOOP: missing forms after DO: permitted by CLtL2, forbidden by ANSI
         CL.
Real time: 0.969 sec.
Run time: 0.969 sec.
Space: 10409084 Bytes
GC: 15, GC time: 0.172 sec.
NIL
[3]>

dsm:в вашем коде есть пара странных вещей здесь.Обратите внимание , что

(loop for x from 1 to 100000
  for y from 1 to 100000 do
  collect `(,x . ,y))

эквивалентно:

(loop for x from 1 to 100
   collecting (cons x x))

что, вероятно, не совсем то, что вы предполагали.Обратите внимание на три вещи:Во-первых, судя по тому, как вы это написали, x и y играют одну и ту же роль.Вероятно, вы имели в виду вложенные циклы.Во-вторых, ваше do после y неверно, так как после него нет формы lisp.В-третьих, вы правы в том, что здесь можно было бы использовать метод обратной привязки, но это делает ваш код более трудным для чтения и не идиоматичным без какой-либо выгоды, поэтому его лучше избегать.

Догадываясь о том, что вы на самом деле задумали, вы могли бы сделать что-то вроде этого (используя цикл):

(loop for x from 1 to 100 appending 
  (loop for y from 1 to 100 collecting (cons x y)))

Если вам не нравится макрос цикла (например, Kyle), вы можете использовать другую конструкцию итерации, например

(let ((list nil)) 
   (dotimes (n 100) ;; 0 based count, you will have to add 1 to get 1 .. 100
     (dotimes (m 100) 
       (push (cons n m) list)))
   (nreverse list))

Если вы обнаружите, что часто занимаетесь подобными вещами, вам, вероятно, следует написать более общую функцию для пересечения списков, а затем передать ей эти списки целых чисел

Если у вас действительно есть проблема с итерацией, а не просто с циклом, вы можете выполнять подобные действия рекурсивно (но обратите внимание, это не scheme, ваша реализация может не гарантировать TCO).Функция "genint", показанная Кайлом здесь является вариантом распространенной (но не стандартной) функции iota.Однако добавление к списку - плохая идея.Эквивалентная реализация, подобная этой:

(defun iota (n &optional (start 0))
  (let ((end (+ n start)))
    (labels ((next (n)
               (when (< n end) 
                 (cons n (next (1+ n))))))
      (next start))))

должно быть намного эффективнее, но все равно это не хвостовой вызов.Обратите внимание, что я настроил это для более обычного значения на основе 0, но дал вам необязательный параметр, начинающийся с 1 или любого другого целого числа.Конечно, вышесказанное можно написать примерно так:

(defun iota (n &optional (start 0))
  (loop repeat n 
     for i from start collecting i))

Преимущество которого заключается в том, что он не раздувает стек для больших аргументов.Если ваша реализация поддерживает устранение хвостового вызова, вы также можете избежать неуместного выполнения рекурсии, выполнив что-то вроде этого:

(defun iota (n &optional (start 0))
  (labels ((next (i list)
             (if (>= i (+ n start))
                 nil
                 (next (1+ i) (cons i list)))))
    (next start nil)))

Надеюсь, это поможет!

Почему бы просто не

(cons x y)

Кстати, я попытался запустить ваш код в CLISP, и он сработал не так, как ожидалось.Поскольку я не большой поклонник макроса цикла, вот как вы могли бы выполнить то же самое рекурсивно:

(defun genint (stop)
  (if (= stop 1) '(1)
      (append (genint (- stop 1)) (list stop))))

(defun genpairs (x y)
  (let ((row (mapcar #'(lambda (y)
                        (cons x y))
                        (genint y))))
    (if (= x 0) row
        (append (genpairs (- x 1) y)
                row))))

(genpairs 100 100)
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top