1

次のようなループからポイント(consed integers)を収集するコードがあります。

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

`(,x . ,y)私の質問は、この状況で使用するのは正しいですか?

編集:このサンプルは、100x100アイテムのテーブルを生成するためのものではありません。ここでのコードは、2つのループ変数の使用とそれらの値の組み合わせを示しています。これを明確にするためにループを編集しました。私が使用する実際のループは他のいくつかの関数に依存している(そしてそれ自体の一部である)ので、呼び出しをリテラル整数に置き換えて、関数からループを引き出す方が理にかなっています。

4

4 に答える 4

7

ただやるほうがはるかに「良い」でしょう(consxy)。

しかし、質問に答えるために、それを行うことには何の問題もありません:)(少し遅くすることを除いて)。

于 2008-09-19T12:39:30.473 に答える
5

ここでの答えはリソースの使用率だと思います(この投稿から)

たとえば、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]>
于 2008-09-19T13:00:00.003 に答える
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))

これはおそらくあなたが意図したものではありません。3 つのことに注意してください。まず、あなたが書いた方法では、x と y は同じ役割を持っています。おそらく、ループをネストするつもりでした。次に、y の後の do は正しくありません。これは、それに続く 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))

この種のことを頻繁に行っていることに気付いた場合は、リストを横断するためのより一般的な関数を作成してから、これらの整数のリストを渡す必要があります。

ループだけでなく、反復に本当に問題がある場合は、この種のことを再帰的に行うことができます (ただし、これはスキームではないことに注意してください。実装によって TCO が保証されない場合があります)。ここでKyle が示した関数「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)))

それが役立つことを願っています!

于 2008-09-19T15:56:26.740 に答える
1

なぜだけではないのですか

(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)
于 2008-09-19T12:39:22.127 に答える