2009-11-02 5 views
0

Il y a quatre ans, j'ai écrit un résolveur de puzzle Sudoku, et maintenant j'essaie de comprendre comment cela fonctionne pour pouvoir réutiliser des pièces de celui-ci pour un solveur de puzzle KenKen. J'ai pensé que je ferais mieux de compactify boucles dans les listes de compréhension et de choisir des noms plus explicites pour les variables.Un cas de listes égales d'ensembles se comportant différemment sous Python 2.5 (je pense ...)

Il existe une classe Puz qui contient le puzzle d'entrée sous la forme d'une liste de (81) chiffres; 1 à 9 où la valeur de la cellule est connue, et 0 où elle ne l'est pas.

La classe Puz contient également une version de travail du puzzle, sauf qu'ici chacun des (81) éléments de la liste est un ensemble; où la réponse pour une cellule est connue, l'ensemble contient une valeur de 1 à 9, par exemple, set ([4]), et où la réponse est inconnue, l'ensemble contient les possibilités restantes, par exemple set ([3,5 , 7,9]). Lorsque Puz._init __ (self, puz) est appelé, les ensembles "maybe" de la liste de travail sont définis sur set ([1,2,3,4,5,6,7,8,9]), et le La première étape pour arriver à une solution consiste à supprimer toutes les valeurs qui apparaissent comme des réponses dans la ligne, la colonne et le bloc 3x3 de cette cellule.

A l'origine, la liste de travail était remplie à l'aide d'une boucle for: de 0 à 80, si c'est une réponse, placez la réponse dans un ensemble, sinon mettez set (range (1, 10)). Je n'arrivais pas à trouver comment obtenir ce type de conditionnel dans une liste de compréhension, donc je l'ai décomposé en une "fonction de remplissage" séparée, dont 3 versions sont montrées ci-dessous. Les fill_funcs diffèrent dans leurs branches « non-réponse » une:

return set(range(1,(self.dim+1))) 
return set(self.r_dim_1_based) 
return self.set_dim_1_based 

Comme vous le voyez, des quantités croissantes de traitement sont déplacés en dehors de la fonction à l'endroit où les petites variables sont initialisés. Le problème est que les deux premières variantes glissent dans le solveur Sudoku et fonctionnent exactement comme le code original. MAIS --- la troisième variation brise, en disant que le 6ème ensemble dans la liste de travail est (ou devient) vide. --- POURTANT la liste des jeux produits par les trois variantes évaluent comme égales:

p.W1 == == p.W2 p.W3 -> True

Je suis perplexe.

est ici un code pour faire la liste des jeux:

#!/usr/bin/env python 
import copy 
from math import sqrt 

''' 
Puzzle #15, from The Guardian: 050624: #41: rated difficult 
''' 
puz = [ 
0,0,1, 9,0,0, 3,0,0, 
0,0,0, 0,0,0, 2,0,0, 
7,6,0, 0,2,0, 0,0,9, 

3,0,0, 0,6,0, 0,0,5, 
0,0,2, 1,0,3, 4,0,0, 
4,0,0, 0,9,0, 0,0,3, 

1,0,0, 0,3,0, 0,9,7, 
0,0,4, 0,0,0, 0,0,0, 
0,0,5, 0,0,8, 6,0,0 
] 

class GroupInfo: pass 

class Puz(GroupInfo): 
    def __init__(self, puz): 
     self.A = copy.deepcopy(puz) 
     self.ncells = len(self.A) 
     self.r_ncells = range(0,self.ncells) 
     self.dim =  int(sqrt(self.ncells)) 
     assert (self.dim ** 2) == self.ncells, "puz is not square" 
     self.r_dim_0_based = range(0,self.dim) 
     self.r_dim_1_based = range(1, self.dim + 1) 
     self.set_dim_1_based = set(self.r_dim_1_based) ## <<---- causes to fail! 
     ##### with 'empty set at W[5]' !?!?!? 

     def W1_fill_func(val): 
      if (val == 0): 
       return set(range(1,(self.dim+1))) 
      else: 
       return set([val]) 

     self.W1 = [ W1_fill_func(self.A[cid]) 
        for cid in self.r_ncells ] 

     def W2_fill_func(val): 
      if (val == 0): 
       return set(self.r_dim_1_based) 
      else: 
       return set([val]) 

     self.W2 = [ W2_fill_func(self.A[cid]) 
        for cid in self.r_ncells ] 

     def W3_fill_func(val): 
      if (val == 0): 
       return self.set_dim_1_based 
      else: 
       return set([val]) 

     self.W3 = [ W3_fill_func(self.A[cid]) 
        for cid in self.r_ncells ] 

     return 
    #def Puz.__init__() 
#class Puz 

p = Puz(puz) 
print p.W1 == p.W2 == p.W3 

Répondre

2

self.W3 que vous avez contient beaucoup avons écrit références au même objet ensemble - dès que vous appelez une méthode mutationniste sur un de ces références, vous avez changé tous les autres. Vous devez vous assurer W3_fill_func renvoie copies indépendantes de l'ensemble d'intérêt, tout comme tous les autres, par exemple. en changeant son retour à return set(self.set_dim_1_based).

+0

C'était rapide, Alex! J'étais encore en train d'éditer le titre! Donc, inutile d'essayer d'éliminer tout le traitement de la ligne 'retour'; D'une façon ou d'une autre, je vais devoir appeler set(). Mais, un ensemble d'un ensemble est toujours un ensemble, et pas une sorte de "ensemble imbriqué". Raffiniert, sehr raffiniert. Et je ne pense pas que j'aurais trouvé cette explication pour un temps looooong. – behindthefall

+0

Aussi semble-t-il être pure chance que cette 'mutation de l'une des références à l'objet modifie cet objet' (ce qui bien sûr, quand je l'écris comme ça, je le reconnais, mais quand il est enterré dans une initialisation ce cas) ne s'est pas levé et m'a mordu avant. Comme dans, ailleurs dans ce solveur de Sudoku. – behindthefall

+0

A droite, 'set (someset)' est juste une copie superficielle (comme 'someset.copy()' mais je préfère appliquer l'idiome uniforme et sympa pour "appeler le type pour faire un idiome (peu profond)"; -). Quoi qu'il en soit, toujours heureux d'aider! –

Questions connexes