2009-11-09 50 views
2

我的lisp代碼的某些部分出現問題。這是一個數獨表格生成器。它正常工作,直到這一部分:數獨表發生器故障,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和我需要插入的元素。我觀察這個元素和列的子矩陣,然後選擇上面沒有出現的數字並將其放在那裏。這是「挑選」變量。問題是有時候「選擇」變量會得到NIL值,儘管在條目循環中它有正確的值。當它得到NIL時,pick值與上一次循環時保持不變(我正在循環的列和行中,在這段代碼上面),使得我的最終表的輸出無效(例如,一行中的double值)。我如何跟蹤選擇變量的變化?我只在這個片段中使用它,我不明白爲什麼它突然改變爲零。

例如,我通常有:

  • 在條目循環:選擇(5)
  • 缺貨條目循環的:選擇(5)
  • 在條目循環:選擇(6 7)
  • 輸入條件循環:選項(6 7)之後,這一個:
  • 條目loop:choices nil。

謝謝。

回答

0

這不是一個正確的答案,但我沒有固定的縮進使代碼有點更清晰的對自己和其他回答者:

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

問題:

  1. 是條目列表列表?每個列表是否代表一行?
  2. 「sub」和「col」的值是多少?
+0

不,條目是在子和列中找不到的元素的列表。 Col表示我在當前循環中的列,sub表示棋盤的子矩陣。具有

 (1 1 1 1 1) (2 2 2 2 2) (3 3 3 3 -1) 
作爲表,對於迭代3,col表示(1 2 -1)和sub((2 2 2)(3 3 -1)) – Manticore

1

潛在的麻煩來源是NCONC。

  • nconc破壞性修改第一個列表。如果不需要,請使用APPEND。

NCONC的第二個問題是使用文字列表。

實施例:

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

這裏「(1 2 3)是文字列表。 Common Lisp中沒有定義破壞性修改這種列表的影響。因此應該避免。該怎麼做呢?

  1. 缺點列表:(列表1 2 3)
  2. 複製文字列表:(複製列表升)
  3. 使用非破壞性操作(APPEND代替NCONC,...)
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)))) 

然後,如果你不需要的if的替代條款,但希望有一個progn,你可以使用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)))) 

最後兩個if條款是相互排斥的,因此要麼cond要麼case是合適的(我將使用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設置爲一個合理的默認開始,你應該也許有一個默認情況下(這可能是你的問題之一):

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

而不是使用setqnconc的,你可以使用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) 

爲了不計算(length choices)兩次,我們可以將其寫入let(這需要成爲let*串行評估):

(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) 
+0

更多:獲取SETQ。基本上這個片段是PICK函數的主體。首先,而不是CAR。不要計算長度兩次。 –

1

我Lisp是相當生疏,但我沒有在那裏看到任何回溯...我認爲你不能隨便開始放數字,並期望他們會做出適當的數獨遊戲。

看起來該列表是零,因爲沒有可能的選項,因此沒有創建。你應該處理。