Случай внешне равных списков множеств, ведущих себя по-разному в Python 2.5 (я думаю ...) - PullRequest
0 голосов
/ 02 ноября 2009

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

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

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

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

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

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

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

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

1 Ответ

2 голосов
/ 02 ноября 2009

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

...