2

lispコードの一部に問題があります。数独テーブルジェネレーターです。この部分までは正常に機能します。

(loop for e in entries do     
    (if (and (not (member e sub))
             (not (member e col)))
        (progn (setq choices (nconc choices (list e)))
               (print choices)))
    (if (= (length choices) 1)
        (setq pick (car choices))
        (if (not (= (length choices) 0)) 
            (setq pick (nth (random (+ 0 (length choices))) choices))))

基本的に、私は行xと列yにいて、要素を挿入する必要があります。この要素と列の部分行列を監視し、上記のいずれにも表示されていない番号を選択してそこに配置します。それが「ピック」変数です。問題は、エントリループでは正しい値を持っていても、「choices」変数がNIL値を取得することがあることです。NILを取得すると、ピック値は最後のループ(このスニペットの上の列と行でループしています)と同じままになり、最終的なテーブルの出力が無効になります(たとえば、行の値が2倍になります)。choices変数がどこで変化するかを追跡するにはどうすればよいですか?私はこのスニペットでのみ使用していますが、なぜ突然nilに変わるのかわかりません。

たとえば、私は通常以下を持っています:

  • エントリループ内:選択肢(5)
  • エントリ外ループ:選択肢(5)
  • エントリループ内:選択肢(6 7)
  • エントリ外ループ:選択肢(6 7)そしてその後これ:
  • エントリループ内:choicesnil。

ありがとうございました。

4

4 に答える 4

3

まず、いくつかの再フォーマット:

(loop for e in entries do     
  (if (and (not (member e sub))
           (not (member e col)))
      (progn (setq choices (nconc choices (list e))) 
             (print choices)))
(if (= (length choices) 1)
    (setq pick (car choices))
(if (not (= (length choices) 0))
    (setq pick (nth (random (+ 0 (length choices))) choices))))

次に、 の代替句は必要ないが が必要な場合はifprogn次を使用できますwhen

(loop for e in entries do     
  (when (and (not (member e sub))
             (not (member e col)))
    (setq choices (nconc choices (list e))) 
    (print choices))
(if (= (length choices) 1)
    (setq pick (car choices))
(if (not (= (length choices) 0))
    (setq pick (nth (random (+ 0 (length choices))) choices))))

最後の 2 つのif句は相互に排他的であるため、またはのいずれcondcaseが適切です (ここでは使用condします)。

(loop for e in entries do     
  (when (and (not (member e sub))
             (not (member e col)))
    (setq choices (nconc choices (list e))) 
    (print choices))
(cond ((= (length choices) 1)
       (setq pick (car choices)))
      ((not (= (length choices) 0))
       (setq pick (nth (random (+ 0 (length choices))) choices))))

zerop述語があります:

(loop for e in entries do     
  (when (and (not (member e sub))
             (not (member e col)))
    (setq choices (nconc choices (list e))) 
    (print choices))
(cond ((= (length choices) 1)
       (setq pick (car choices)))
      ((not (zerop (length choices)))
       (setq pick (nth (random (+ 0 (length choices))) choices))))

一部の値に 0 を追加すると何が達成されるかわかりません。

(loop for e in entries do     
  (when (and (not (member e sub))
             (not (member e col)))
    (setq choices (nconc choices (list e))) 
    (print choices))
(cond ((= (length choices) 1)
       (setq pick (car choices)))
      ((not (zerop (length choices)))
       (setq pick (nth (random (length choices)) choices))))

最初から賢明なデフォルトに設定されていることが確実でない限りpick、おそらくデフォルトのケースを使用する必要があります(これは問題の1つかもしれません):

(loop for e in entries do     
  (when (and (not (member e sub))
             (not (member e col)))
    (setq choices (nconc choices (list e))) 
    (print choices))
(cond ((= (length choices) 1)
       (setq pick (car choices)))
      ((not (zerop (length choices)))
       (setq pick (nth (random (length choices)) choices)))
      (t
       (setq pick nil))

setqandを使用する代わりにnconc、次を使用できますpush(これにより、新しい要素がリストの先頭に配置されますが、ランダムに選択するため、これは問題になりません)。

(loop for e in entries do     
  (when (and (not (member e sub))
             (not (member e col)))
    (push e choices)
    (print choices))
(cond ((= (length choices) 1)
       (setq pick (car choices)))
      ((not (zerop (length choices)))
       (setq pick (nth (random (length choices)) choices)))
      (t
       (setq pick nil))

このスニペットの開始時は でchoicesあるはずで、このスニペットの後()は不要choicesであり、印刷はデバッグのためだけのものであると思われるため、条件を使用して変更することchoicesにより、別の方法でこれを行うことができます。remove-if

(let ((choices (remove-if (lambda (e)
                            (or (member e sub)
                                (member e col)))
                          entries)))
  (print choices)
  (cond ((= (length choices) 1)
         (setq pick (car choices)))
        ((not (zerop (length choices)))
         (setq pick (nth (random (length choices)) choices)))
        (t
         (setq pick nil)))

choicesが今のように出力された場合()、ここには選択肢が残っていないことを意味するため、バックトラックを行う必要があります (または、行き止まりに達したときにアルゴリズムが行うことは何でも)。

最後に、(length choices)非負の整数のみを使用できるため、異なる順序でケースをテストする場合のcase代わりに使用できます。cond

(let ((choices (remove-if (lambda (e)
                            (or (member e sub)
                                (member e col)))
                          entries)))
  (print choices)
  (case (length choices)
    (0 (setq pick nil))
    (1 (setq pick (car choices)))
    (otherwise (setq pick (nth (random (length choices)) choices)))))

リクエストにより更新。

Rainer が指摘するように、これは基本的にpick関数の本体であるため、すべての自由変数を取り除くことができます。また、 の代わりにcar、(リストの場合) よりわかりやすい名前 を使用できますfirst

(defun pick (entries sub col)
  (let ((choices (remove-if (lambda (e)
                              (or (member e sub)
                                  (member e col)))
                            entries)))
    (print choices)
    (case (length choices)
      (0 nil)
      (1 (first choices))
      (otherwise (nth (random (length choices)) choices)))))

この関数は別の場所で定義され、スニペットの場所では次のように呼び出されます。

(pick entries sub col)

2 回計算しないようにするために、それを (シリアル評価に必要な)(length choices)に入れることができます。letlet*

(defun pick (entries sub col)
  (let* ((choices (remove-if (lambda (e)
                               (or (member e sub)
                                   (member e col)))
                             entries))
         (choices-length (length choices)))
    (print choices)
    (case choices-length
      (0 nil)
      (1 (first choices))
      (otherwise (nth (random choices-length) choices)))))

最後のステップ (実際にはオプションですが、選択肢を減らすシーケンスがさらにあることに気付くかもしれませんrow) は、少し一般化したものです。

(defun pick (entries &rest exclusion-sequences)
  (let* ((choices (remove-if (lambda (e)
                               (some #'identity
                                     (mapcar (lambda (seq)
                                               (member e seq))
                                             exclusion-sequences)))
                             entries))
         (choices-length (length choices)))
    (print choices)
    (case choices-length
      (0 nil)
      (1 (first choices))
      (otherwise (nth (random choices-length) choices)))))

この関数の呼び出しは同じ形ですが、除外シーケンスをいくつでも使用できるようになりました。

(pick entries col sub row ver ima fou)
于 2009-11-10T08:51:59.063 に答える
1

問題の潜在的な原因は NCONC です。

  • nconc は、最初のリストを破壊的に変更しています。それが望ましくない場合は、代わりに APPEND を使用してください。

NCONC の問題の 2 つ目の原因は、リテラル リストの使用です。

例:

(defun foo (bar)  (let ((l '(1 2 3))) ...))

ここで、'(1 2 3) はリテラル リストです。このようなリストを破壊的に変更した場合の影響は、Common Lisp では定義されていません。したがって、それは避けるべきです。代わりに何をしますか?

  1. cons リスト: (list 1 2 3)
  2. リテラル リストをコピーします: (copy-list l)
  3. 非破壊的な操作を使用する (NCONC の代わりに APPEND など)
于 2009-11-10T00:09:49.647 に答える
1

私の Lisp はかなり錆びていますが、後戻りは見られません...そして、ランダムに数字を入れ始めて、適切な数独ゲームができると期待することはできないと思います。

可能なオプションがなく、作成されていないため、リストは nil のようです。あなたはそれを処理する必要があります。

于 2009-11-10T09:01:18.327 に答える