Est-il correct d'utiliser l'idiome backtick / comma à l'intérieur d'une (boucle…)?

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

  •  01-07-2019
  •  | 
  •  

Question

J'ai un code qui collecte les points (entiers consed) d'une boucle qui ressemble à ceci:

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

Ma question est la suivante: est-il correct d'utiliser `(, x., y) dans cette situation?

Éditer: Cet exemple ne concerne pas la génération d’une table d’éléments 100x100, le code ici n’est qu’une illustration de l’utilisation de deux variables de boucle et de la consignation de leurs valeurs. J'ai édité la boucle pour le rendre clair. La boucle que j’utilise dépend de plusieurs autres fonctions (et fait partie de celle-ci); il était donc plus logique de remplacer les appels par des entiers littéraux et de retirer la boucle de la fonction.

Était-ce utile?

La solution

Il serait préférable de le faire (contre x y).

Mais pour répondre à la question , rien de mal à cela :) (sauf que cela ralentisse un peu).

Autres conseils

Je pense que la réponse ici est l'utilisation des ressources (issue de Cet article )

par exemple dans 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: votre code présente quelques problèmes étranges ici . Notez que

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

est équivalent à:

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

ce qui n'est probablement pas tout à fait ce que vous vouliez. Notez trois choses: premièrement, comme vous l'avez écrit, x et y ont le même rôle. Vous avez probablement voulu nidifier des boucles. Deuxièmement, votre mot de passe après le y est incorrect, car aucune forme lisp ne le suit. Troisièmement, vous avez raison de dire que vous pouvez utiliser l’approche backtick ici, mais cela rend votre code plus difficile à lire et non idiomatique sans gain, il est donc préférable de l’éviter.

En devinant ce que vous vouliez réellement, vous pourriez faire quelque chose comme ceci (en utilisant la boucle):

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

Si vous n'aimez pas la macro de la boucle (comme Kyle), vous pouvez utiliser une autre construction d'itération telle que

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

Si vous vous trouvez souvent en train de faire ce genre de chose, vous devriez probablement écrire une fonction plus générale pour traverser des listes, puis lui transmettre ces listes d'entiers

Si vous avez vraiment un problème d'itération, pas seulement de boucle, vous pouvez le faire de manière récursive (mais notez bien qu'il ne s'agit pas d'un schéma, votre implémentation peut ne pas garantir le coût total de possession). La fonction " genint " présenté par Kyle ici est une variante d'une fonction iota commune (mais pas standard). Cependant, l'ajout à la liste est une mauvaise idée. Une implémentation équivalente comme celle-ci:

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

devrait être beaucoup plus efficace, mais ce n’est toujours pas un appel de queue. Notez que j'ai configuré ceci pour le plus habituel basé sur 0, mais je vous ai donné un paramètre optionnel pour commencer à 1 ou à un autre entier. Bien sûr, ce qui précède peut être écrit quelque chose comme:

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

Qui a l’avantage de ne pas vider la pile pour les gros arguments. Si votre implémentation prend en charge l'élimination des appels en fin de ligne, vous pouvez également éviter que la récursion ne soit à sa place en procédant comme suit:

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

J'espère que ça aide!

Pourquoi ne pas simplement

(cons x y)

Au fait, j’ai essayé d’exécuter votre code dans CLISP et cela n’a pas fonctionné comme prévu. Puisque je ne suis pas un grand fan de la macro de la boucle, voici comment vous pouvez accomplir la même chose de manière récursive:

(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)
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top