2009-11-02 2 views
0

Четыре года назад я написал головоломку для судоку, и теперь я пытаюсь понять, как она работает, чтобы я мог повторно использовать части это для решения головоломки KenKen. Я думал, что лучше компактифицировать циклы в контекстах списков и выбрать более понятные имена для переменных.Случай с равными по внешнему виду списками множеств, которые ведут себя по-другому под Python 2.5 (я думаю ...)

Существует класс Puz, который содержит входную головоломку в виде списка (81) цифр; 1-9, где значение ячейки известно, а 0 - нет.

Класс Puz также содержит рабочую версию головоломки, за исключением того, что здесь каждый из (81) элементов в списке представляет собой набор; где ответ для ячейки известен, набор содержит одно значение от 1 до 9, например, set ([4]), и если ответ неизвестен, набор содержит оставшиеся возможности, например, set ([3,5 , 7,9]). Когда вызывается Puz._init __ (self, puz), эти «возможно» наборы в рабочем списке устанавливаются как [1,2,3,4,5,6,7,8,9]), а первым шагом в решении проблемы является вычеркнуть все значения, которые появляются в виде ответов в строке, столбце и блоке 3x3 этой ячейки.

Первоначально рабочий список был заполнен с использованием цикла for: от 0 до 80, если это ответ, вставить ответ в виде набора, иначе положить в набор (диапазон (1, 10)). Я не мог понять, как получить это условное выражение в понимании списка, поэтому я разбил его на отдельную «функцию заполнения», из которых 3 версии показаны ниже. В fill_funcs различаются по их «не-ан-ответа ветвей»:

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

Как вы видите, все большее количество обработки перемещаются за пределы функции туда, где маленькие переменные инициализируются.

Беда в том, что первые два варианта скользят в решатель Судоку и работают точно так, как это сделал оригинальный код. НО --- третья вариация ломается, говоря, что 6-й набор в рабочем списке (или становится) пустым. ПОКА --- списки множеств, полученных всеми тремя вариантами оценки равными:

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

Я в тупик.

Вот некоторый код для создания списков множеств:

#!/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 

ответ

2

self.W3 как вы закодированы в нем содержится много ссылок на те же множество объекты - как только вы вызываете любой метод мутирует на одном из этих ссылок вы изменили все остальные. Вы должны убедиться, что W3_fill_func возвращает независимых копий представляющих интерес объектов, как и все остальные, например. путем изменения его возврата на return set(self.set_dim_1_based).

+0

ЭТО было быстро, Алекс! Я все еще редактировал название! Таким образом, бессмысленно пытаться устранить всю обработку из строки «возврат»; так или иначе, мне придется вызвать set(). Но набор множеств все еще является множеством, а не каким-то «вложенным множеством». Raffiniert, sehr raffiniert. И я не думаю, что я бы наткнулся на это объяснение для looooong времени. – behindthefall

+0

Также кажется, что эта «мутация любой ссылки на объект меняет этот объект» (что, конечно, когда я пишу это так, я узнаю, но когда он похоронен в инициализации, я не видел в этот случай) не поднялся и укусил меня раньше. Как и в других местах этого решателя Судоку. – behindthefall

+0

Правильно, 'set (someset)' - это только мелкая копия (точно так же, как 'someset.copy()', но я предпочитаю применять единообразную и приятную идиому, чтобы «вызвать тип, чтобы сделать (мелкую) копию« идиомой »широко; -). В любом случае, всегда рады помочь! –