Para responder à questão, vou me basear no código fornecido nos slides da aula 7, com algumas modificações. Primeiro, defini o método mágico call, para que se possa visualizar a fila sem ter que utilizar print. Por exemplo, após gerar um objeto da classe fila (a = Queue()), podemos visualizar o objeto com o comando a(). A segunda diferença foi que mantive a notação do livro do Goodrich e nomeei o método de adicionar elementos à fila de enqueue e o de retirar elementos de dequeue.
Partindo para a resposta, temos que permitir que o usuário defina a capacidade máxima da fila ao criar o objeto, mas esse parâmetro não precisa ser obrigatório. Assim, se não for escolhido um valor para maxlen, a classe se comporta como no código original. Isso pode ser implementado da seguinte maneira:
def __init__(self, maxlen=None):.
self.__maxlen=maxlen if maxlen is not None else None
self.__queue=[None]*self.__maxlen if maxlen is not None else [None]*2
A seguir, é necessário criar uma exceção toda vez que se deseja acrescentar um elemento a uma fila que já esteja cheia. Antes, quando isso acontecia, dobrava-se a capacidade máxima da lista a partir da ideia de circularidade. Agora, devemos retornar uma mensagem avisando que a capacidade está em seu limite:
def enqueue(self,element):
if self.__maxlen is not None and (self.__size==len(self.__queue)):
print("Queue is at its full capicity")
Por último, defino também um método que permite ao usuário aumentar a capacidade da fila sem ter que criar um novo objeto. Na verdade, esse método já estava incluso na classe, mas originalmente fora definido com dois underscores, o que significa que o método é privado e só pode ser utilizada dentro da classe. O que fiz foi simplesmente definir um novo método acessível ao usuário e que chame o método privado. Note que caso se forneça uma capacidade menor que o tamanho atual da fila, uma mensagem de erro é gerada:
def maxlen(self,newmaxlen): # Método para mudar a capacidade máxima de uma fila já criada
self.__maxlen=newmaxlen
self.__resize(newmaxlen)
def __resize(self,newCapacity):
if newCapacity == None:
self.__maxlen = newCapacity
elif newCapacity != None and newCapacity < self.__size:
print ("New capacity below current capacity")
else:
old=self.__queue
self.__queue=[None]*newCapacity
theElementPosition=self.__front
for i in range(self.__size):
self.__queue[i]=old[theElementPosition]
theElementPosition=(1+theElementPosition)%len(old)
self.__front=0
Código completo para a classe Queue com restrição de capacidade:
class Queue:
def __init__(self, maxlen=None): # Inicialização da classe. Se o usuário não escolher um comprimento máximo, a classe funciona exatamente como a função original.
self.__maxlen=maxlen if maxlen is not None else None
self.__queue=[None]*self.__maxlen if maxlen is not None else [None]*2 # Ou a fila assume o tamanho máximo ou tamanho 2, como antes.
self.__size=0
self.__front=0
def __call__(self): # Uma das formas de se "chamar" o elemento criado
return self.__queue
def __len__(self):
return self.__size
def maxlen(self,newmaxlen): # Método para mudar a capacidade máxima de uma fila já criada
self.__maxlen=newmaxlen
self.__resize(newmaxlen)
def is_empty(self):
return self.__size==0
def enqueue(self,element):
if self.__maxlen is not None and (self.__size==len(self.__queue)): # A primeira exceção é a de capacidade cheia. Caso não esteja no limite, o código funciona como antes.
print("Queue is at its full capicity")
else:
if (self.__size==len(self.__queue)):
self.__resize(2*len(self.__queue))
position=(self.__front + self.__size)%len(self.__queue)
self.__queue[position]=element
self.__size=self.__size+1
def first(self):
if(self.is_empty()):
print('Queue is empty')
else:
return self.__queue[self.__front]
def dequeue(self): # Maxlen não interfere no dequeue, que segue igual
if(self.is_empty()):
print('Queue is empty')
else:
firstElement=self.__queue[self.__front]
self.__queue[self.__front]=None
self.__front=(self.__front+1)%len(self.__queue)
self.__size=self.__size-1
if 0 < self.__size < len(self.__queue)//4:
self.__resize(len(self.__queue)//2)
print ("Position of the first element", self.__front)
return firstElement
def __resize(self,newCapacity):
if newCapacity == None:
self.__maxlen = newCapacity
elif newCapacity != None and newCapacity < self.__size:
print ("New capacity below current capacity")
else:
old=self.__queue
self.__queue=[None]*newCapacity
theElementPosition=self.__front
for i in range(self.__size):
self.__queue[i]=old[theElementPosition]
theElementPosition=(1+theElementPosition)%len(old)
self.__front=0
Testando alguns comandos:
if __name__=="__main__":
a=Queue(5)
a()
a.enqueue(3)
a.enqueue(4)
a.enqueue(5)
a.enqueue(6)
a.enqueue(7)
# Agora, a fila está cheia. Note:
a.enqueue(8)
# Se o usuário quiser alterar a capacidade da fila sem ter que recriar o objeto, basta usar o método maxlen:
a.maxlen(10)
a.maxlen(None)