PDA

View Full Version : [Python] Liste linkata


~FullSyst3m~
16-12-2008, 01:42
Come ho già detto in altre occasioni ultimamente il libro sul quale sto studianto è drasticamente calato dal punto di vista qualità e correttezza di informazione, sto passando più tempo a correggere gli errori e a capire che cosa vuole dire il libro, che a studiare.

Paragrafo 17.7:

Cosa succede se invochi questo metodo e passi una lista composta da un solo elemento (elemento singolo)? Cosa succede se passi come argomento una lista vuota? C'è una precondizione per questo metodo? Se esiste riscrivi il metodo per gestire gli eventuali problemi.

Il metodo è il seguente:

def RimuoviNodo(Lista):
if Lista == None: return
Secondo = Lista.ProssimoNodo
# Il primo nodo deve riferirsi al terzo
Lista.ProssimoNodo = Secondo.ProssimoNodo
# Separa il secondo nodo dal resto della lista
Secondo.ProssimoNodo = None
return Secondo

E questa è la classe Nodo, le varie assegnazioni e chiamate:

class Nodo:
def __init__(self, Contenuto=None, ProssimoNodo=None):
self.Contenuto = Contenuto
self.ProssimoNodo = ProssimoNodo

def __str__(self):
return str(self.Contenuto)

def StampaLista(Nodo):
Lista = []
while Nodo:
Lista.append(Nodo.Contenuto)
Nodo = Nodo.ProssimoNodo
print Lista

Nodo1 = Nodo(1)
Nodo2 = Nodo(2)
Nodo3 = Nodo(3)

Nodo1.ProssimoNodo = Nodo2
Nodo2.ProssimoNodo = Nodo3

StampaLista(Nodo1)
Rimosso = RimuoviNodo(Nodo1)
StampaLista(Rimosso)
StampaLista(Nodo1)

Io ho provato a lasciare il Nodo1 soltanto e ho tolto Nodo1.ProssimoNodo e Nodo2.ProssimoNodo. Nonostante ci sia il controllo iniziale, il controllo viene bypassato e ovviamente mi viene restituito l'errore che None non ha un attributo ProssimoNodo. Poi ho modificato Nodo1 in Nodo1 = Nodo() cosi da provare il secondo caso che ha detto il libro e ottengo anche qua lo stesso errore.
Premesso tutto questo, non capisco perchè dovrei riscrivere il metodo (come dice il libro) visto che quando viene spiegata la precondizione c'è messo chiaramente che in questi casi viene usata per imporre una limitazione ai parametri, infatti nel codice successivo non c'è nessuna correzione per gestire gli eventuali problemi.
Potrei usare un try/except magari, ma il punto non è questo. Spero si sia capito cosa voglio dire.
Dopo che ho riavviato non dà più l'errore di prima(solo con la funzione RimuoviNodo ovviamente dà errore), però il controllo non viene eseguito lo stesso perchè anche se il nodo che passo vale None viene stampato il valore (None), e per stamparlo dovrebbe entrare nell'if, che però ha come condizione != da None, quindi non dovrebbe essere eseguito. Mi riferisco a questo:

def StampaInversa(Nodo):
if Nodo == None: return
StampaInversa(Nodo.ProssimoNodo)
print Nodo,

def StampaInversaFormato(Lista):
print "[",
if Lista != None:
Coda = Lista.ProssimoNodo
StampaInversa(Coda)
print Lista,
print "]",

Nodo1 = Nodo()
StampaInversa(Nodo1)
StampaInversaFormato(Nodo1)

Output:

None [ None ]

Ma indovinate un pò? Dopo aver modificato il codice come scriverò tra poco sotto non mi stampa più None perchè ora il controllo viene eseguito e funziona. L'unica soluzione che mi viene in mente è che possa essere un problema di IDE (sono sul portatile e uso PyScripter).

Infine, sempre tratto dal libro:

Creiamo innanzitutto una nuova classe chiamata ListaLinkata. I suoi attributi sono un intero che contiene la lunghezza della lista e il riferimento al primo nodo. Gli oggetti ListaLinkata ci serviranno per gestire liste di oggetti Nodo:

class ListaLinkata:
def __init__(self) :
self.Lunghezza = 0
self.Testa = None

Una cosa positiva per quanto concerne la classe ListaLinkata è che fornisce un posto naturale dove inserire funzioni contenitore quale StampaInversaFormato e che possiamo far diventare metodi della classe:

class ListaLinkata:
...
def StampaInversa(self):
print "[",
if self.Testa != None:
self.Testa.StampaInversa()
print "]",

class Nodo:
...
def StampaInversa(self):
if self.ProssimoNodo != None:
Coda = self.ProssimoNodo
Coda.StampaInversa()
print self.Contenuto,

Per rendere le cose più interessanti, rinominiamo StampaInversaFormato. Ora abbiamo due metodi chiamati StampaInversa: quello nella classe Nodo che è l'aiutante e quello nella classe ListaLinkata che è il contenitore. Quando il metodo contenitore invoca self.Testa.StampaInversa sta in effetti invocando l'aiutante dato che self.Testa è un oggetto di tipo Nodo

Dove lo vede l'oggetto di tipo Nodo?

DanieleC88
16-12-2008, 11:09
def RimuoviNodo(Lista):
if Lista == None: return
Secondo = Lista.ProssimoNodo
# Il primo nodo deve riferirsi al terzo
Lista.ProssimoNodo = Secondo.ProssimoNodo
# Separa il secondo nodo dal resto della lista
Secondo.ProssimoNodo = None
return Secondo
Usando Lista.ProssimoNodo = Secondo.ProssimoNodo stai effettivamente dicendo: Lista.ProssimoNodo = Lista.ProssimoNodo.ProssimoNodo. Tu ti stai assicurando che Lista non sia None, ma chi ti dice che non lo sia anche Lista.ProssimoNodo? È per questo che con un solo nodo ti dava un errore perché provavi ad accedere ad un oggetto di tipo None, anche con quel controllo iniziale.

Premesso tutto questo, non capisco perchè dovrei riscrivere il metodo (come dice il libro) visto che quando viene spiegata la precondizione c'è messo chiaramente che in questi casi viene usata per imporre una limitazione ai parametri, infatti nel codice successivo non c'è nessuna correzione per gestire gli eventuali problemi.
Potrei usare un try/except magari, ma il punto non è questo. Spero si sia capito cosa voglio dire.
In realtà no, non ho capito. :D

però il controllo non viene eseguito lo stesso perchè anche se il nodo che passo vale None viene stampato il valore (None), e per stamparlo dovrebbe entrare nell'if, che però ha come condizione != da None, quindi non dovrebbe essere eseguito. Mi riferisco a questo:

def StampaInversa(Nodo):
if Nodo == None: return
StampaInversa(Nodo.ProssimoNodo)
print Nodo,

def StampaInversaFormato(Lista):
print "[",
if Lista != None:
Coda = Lista.ProssimoNodo
StampaInversa(Coda)
print Lista,
print "]",

Nodo1 = Nodo()
StampaInversa(Nodo1)
StampaInversaFormato(Nodo1)

Output:

None [ None ]
Stai sempre attento a ciò che fai, il risultato è perfettamente corretto: solo che non te ne sei accorto. :D
Guarda:
class Nodo:
def __init__(self, Contenuto=None, ProssimoNodo=None):
self.Contenuto = Contenuto
self.ProssimoNodo = ProssimoNodo

def __str__(self):
return str(self.Contenuto)

Quando chiedi la stampa di un nodo lui ti restituisce come stringa da stampare la conversione in stringa del suo contenuto. Dal canto suo, il contenuto è inizializzato solo nel costruttore, e di default è impostato a None. Quando quel codice di prima attraversa la lista, trova che c'è effettivamente un nodo da stampare, e prova a stamparne il contenuto... che però è None (dal momento che a Nodo1 è stato assegnato un oggetto Node() senza un contenuto specifico, che quindi di default è None). In definitiva, ciò che viene stampato è:
print str(None),

Dove lo vede l'oggetto di tipo Nodo?
La classe ListaLinkata evidentemente aggiunge man mano oggetti di tipo Nodo a self.Testa quando tu le chiedi l'inserimento di un nuovo elemento. Così, quando vai a stampare tutto funziona come previsto (ad ogni nodo incontrato, l'oggetto di tipo ListaLinkata dice al nodo di stamparsi).

~FullSyst3m~
16-12-2008, 12:17
Usando Lista.ProssimoNodo = Secondo.ProssimoNodo stai effettivamente dicendo: Lista.ProssimoNodo = Lista.ProssimoNodo.ProssimoNodo. Tu ti stai assicurando che Lista non sia None, ma chi ti dice che non lo sia anche Lista.ProssimoNodo? È per questo che con un solo nodo ti dava un errore perché provavi ad accedere ad un oggetto di tipo None, anche con quel controllo iniziale.

Che significa Lista.ProssimoNodo.ProssimoNodo? Comunque questo, come ho già scritto, è normale che dà l'errore se passo ad esempio solo Nodo(1)

In realtà no, non ho capito. :D

In poche parole gli errori che mi davano le funzioni StampaInversa e StampaInversaFormato dopo che ho riavviato non me li dava più nè con la lista vuota nè con un nodo singolo


Stai sempre attento a ciò che fai, il risultato è perfettamente corretto: solo che non te ne sei accorto. :D
Guarda:
class Nodo:
def __init__(self, Contenuto=None, ProssimoNodo=None):
self.Contenuto = Contenuto
self.ProssimoNodo = ProssimoNodo

def __str__(self):
return str(self.Contenuto)

Quando chiedi la stampa di un nodo lui ti restituisce come stringa da stampare la conversione in stringa del suo contenuto. Dal canto suo, il contenuto è inizializzato solo nel costruttore, e di default è impostato a None. Quando quel codice di prima attraversa la lista, trova che c'è effettivamente un nodo da stampare, e prova a stamparne il contenuto... che però è None (dal momento che a Nodo1 è stato assegnato un oggetto Node() senza un contenuto specifico, che quindi di default è None). In definitiva, ciò che viene stampato è:
print str(None),


Fattostà che sempre dopo il famoso riavvio non c'è più traccia di nessun None

La classe ListaLinkata evidentemente aggiunge man mano oggetti di tipo Nodo a self.Testa quando tu le chiedi l'inserimento di un nuovo elemento. Così, quando vai a stampare tutto funziona come previsto (ad ogni nodo incontrato, l'oggetto di tipo ListaLinkata dice al nodo di stamparsi).

E' vero, pensavo che il metodo per aggiungere un nodo era nella classe Nodo. Mi sta dando non pochi problemi sto libro ultimamente

DanieleC88
16-12-2008, 13:32
Che significa Lista.ProssimoNodo.ProssimoNodo?
È l'oggetto puntato dal campo ProssimoNodo dell'oggetto puntato dal campo ProssimoNodo dell'oggetto Lista. Detto così sembra una cosa contorta, prova a farti un piccolo disegno su carta dei collegamenti tra gli oggetti e lo capirai in un batter d'occhio.
E' vero, pensavo che il metodo per aggiungere un nodo era nella classe Nodo. Mi sta dando non pochi problemi sto libro ultimamente
Non è il libro, sono i concetti che sono un po' più complessi. Devi imparare a "pensare ad oggetti". :D
Se invece della classe ListaLinkata (che colleziona oggetti Nodo) ci fosse stata una classe GenereUmano (che colleziona oggetti EssereUmano), quale delle due avrebbe dovuto gestire l'aggiunta di un nuovo essere umano? GenereUmano o EssereUmano? :)

Per quanto riguarda poi il fatto del riavvio, io escluderei una possibilità del genere, non si hanno risultati casuali su diverse esecuzioni dello stesso codice quando questo riceve gli stessi input ed esegue le stesse istruzioni. Penso quindi che ti sia sfuggito qualche dettaglio che modificava l'esecuzione prima di riavviare l'IDE, mi sembra l'unica possibilità.

~FullSyst3m~
16-12-2008, 13:57
È l'oggetto puntato dal campo ProssimoNodo dell'oggetto puntato dal campo ProssimoNodo dell'oggetto Lista. Detto così sembra una cosa contorta, prova a farti un piccolo disegno su carta dei collegamenti tra gli oggetti e lo capirai in un batter d'occhio.

Non è il libro, sono i concetti che sono un po' più complessi. Devi imparare a "pensare ad oggetti". :D
Se invece della classe ListaLinkata (che colleziona oggetti Nodo) ci fosse stata una classe GenereUmano (che colleziona oggetti EssereUmano), quale delle due avrebbe dovuto gestire l'aggiunta di un nuovo essere umano? GenereUmano o EssereUmano? :)

Per quanto riguarda poi il fatto del riavvio, io escluderei una possibilità del genere, non si hanno risultati casuali su diverse esecuzioni dello stesso codice quando questo riceve gli stessi input ed esegue le stesse istruzioni. Penso quindi che ti sia sfuggito qualche dettaglio che modificava l'esecuzione prima di riavviare l'IDE, mi sembra l'unica possibilità.

Forse la stai contorcendo senza motivo la spiegazione. Io la vedo più semplice di come la dici tu.

Per quanto riguarda il libro io non ho detto che credevo che il metodo doveva andare nella classe Nodo, ma che avevo letto male io sul libro. In ogni caso il libro in questi ultimi capitoli ha un sacco di errori

E per il riavvio è cosi, ho salvato, riavviato e poi niente più errori. Io credo sia un problema di IDE che alle volte non salva le modifiche e continua ad eseguire il codice precedente

DanieleC88
16-12-2008, 14:12
Forse la stai contorcendo senza motivo la spiegazione. Io la vedo più semplice di come la dici tu.
Ok, allora come la vedi? :)
Non ho contorto niente più del necessario, ho solo spiegato a cosa tentasse di accedere il codice. Va bene vederla semplice, ma non la devi vedere nel modo scorretto, altrimenti non cogli l'errore. ;)

~FullSyst3m~
16-12-2008, 18:08
Ok, allora come la vedi? :)
Non ho contorto niente più del necessario, ho solo spiegato a cosa tentasse di accedere il codice. Va bene vederla semplice, ma non la devi vedere nel modo scorretto, altrimenti non cogli l'errore. ;)

Certamente, ma avevo capito a cosa accedeva il codice. Non era quella l'incertezza ;)

In ogni caso ho dedicato il pomeriggio solo alle liste linkate, ho fatto qualche prova e con i miei strumenti ultra tecnologici (carta e penna) mi sono tolto tutti i dubbi ;)