Controles para avançar e voltar são bem comuns em diversas aplicações computacionais. Galerias de fotos, reprodutores de músicas e vídeos, feeds de notícias e aplicações biomédicas são algumas das várias que usam desse recurso.
a) Implemente uma classe Carrosel que receba uma lista de itens e forneça os métodos avançar(), retroceder(), ir_para_inicio() e ir_para_fim(), além da propriedade item_atual. DICA: use algum atributo privado como cursor para guardar a posição atual na lista.
b) Modifique a classe de modo que o construtor receba um parâmetro circular, do tipo booleano. Quando a opção circular estiver habilitada para um certo carrosel, o mesmo deverá se comportar apropriadamente: uma operação de avançar, quando executada ao final da lista, deverá fazer com que o carrosel volte ao início; analogamente, uma operação de retroceder, quando executada no início da lista, deverá fazer com que o carrosel vá para o último item.

Passo 1

O enunciado pede duas coisas claras:
  • (a) Criar uma classe chamada Carrosel que recebe uma lista de itens e oferece:
    • métodos: avancar(), retroceder(), ir_para_inicio(), ir_para_fim();
    • propriedade: item_atual (que devolve o item onde o cursor está).
    • usar algum atributo privado (por exemplo um cursor) para guardar a posição atual.
  • (b) Modificar a classe para aceitar no construtor um parâmetro booleano circular que, quando True, faz com que:
    • avancar() no final volte ao início;
    • retroceder() no início vá para o último item.
Informações relevantes dadas:
  • Precisamos manter a posição atual (cursor).
  • A classe deve aceitar uma lista de itens no construtor.
  • O comportamento muda se circular for True ou False.
  • Não foi especificado comportamento para lista vazia — devemos tratar isso de forma segura (não gerar erro).

Passo 2

Decisões de projeto (por que escolhi assim):
  • Vou armazenar os itens internamente como uma lista (cópia) para evitar efeitos externos se a lista passada for modificada fora da classe.
  • Usarei um atributo privado por convenção chamado _cursor para indicar o índice atual (0-based).
  • Usarei um atributo _circular (booleano) para guardar se o carrosel é circular.
  • item_atual será implementado como uma propriedade Python (usando @property) para permitir leitura simples, sem expor o cursor diretamente.
  • Para lista vazia, item_atual vai retornar None e os métodos de navegação não farão nada — isso é seguro em prova e evita exceções inesperadas.
  • Complexidade de todas as operações será O(1), o que é desejável (apenas manipulação de índices).

Passo 3

Explicação detalhada do construtor e dos atributos:
  • Construtor recebe dois parâmetros: itens e circular (padrão False).
  • Dentro do construtor:
    • copio a lista com list(itens) para ter uma cópia independente;
    • armazeno o booleano circular em _circular;
    • inicializo _cursor em 0 (começamos no primeiro elemento, se existir).
  • Raciocínio: manter o cursor como índice inteiro facilita acessar o elemento atual com _itens[_cursor].
  • Exemplo mental: se itens = [‘a’,‘b’,‘c’] e _cursor = 1, então item atual é ‘b’.

Passo 4

Implementando a propriedade item_atual (e por que usar property):
  • Uma propriedade permite usar carrosel.item_atual como se fosse um atributo, mas com lógica por trás.
  • Lógica que vou usar:
    • Se a lista estiver vazia (len(_itens) == 0) retorno None;
    • Caso contrário retorno _itens[_cursor].
  • Isso encapsula o acesso e evita exposição do índice.

Passo 5

Implementando avancar() (com explicação passo a passo):
  • Primeiro, se a lista está vazia, retorno imediatamente (nada a fazer).
  • Se o cursor ainda não está no último índice (isto é, _cursor < len(_itens)-1), incremento _cursor em 1 — isso avança um item.
  • Caso contrário (cursor no último índice):
    • Se _circular é True, coloco _cursor = 0 (volto ao início).
    • Se _circular é False, mantenho _cursor no final (operação não faz nada além de ficar no último item).
  • Observação didática: comparar com os limites da lista é importante para evitar index error. Usar len(…) - 1 dá o índice do último elemento.

Passo 6

Implementando retroceder() (com explicação):
  • Se a lista está vazia, retorno imediatamente.
  • Se _cursor > 0, decremento _cursor em 1 (movo para o item anterior).
  • Caso contrário (estamos no índice 0):
    • Se _circular é True, seto _cursor = len(_itens) - 1 (vai para o último).
    • Se _circular é False, mantenho _cursor em 0 (não faz nada).
  • Essa lógica é simétrica à de avancar() e garante comportamento correto no início da lista.

Passo 7

Implementando ir_para_inicio() e ir_para_fim():
  • ir_para_inicio():
    • Se a lista estiver vazia, não faz nada.
    • Caso contrário, seta _cursor = 0.
  • ir_para_fim():
    • Se a lista estiver vazia, não faz nada.
    • Caso contrário, seta _cursor = len(_itens) - 1.
  • Estas operações permitem posicionamento direto, úteis na prova quando pedirem movimentação explícita.

Passo 8

Detalhes finais de implementação e boas práticas:
  • Não exponho _itens nem _cursor diretamente (são privados por convenção).
  • Mantive todos os métodos com comportamento definido em lista vazia (sem lançar exceções).
  • Comentei cada método na implementação para facilitar leitura na prova.
  • Complexidade: todas operações O(1).
  • Vou mostrar agora a implementação final da classe (pronta para usar). Nos exemplos abaixo comentei o comportamento esperado para você testar mentalmente ou em Python.

Passo 9

Exemplos de uso (para sua intuição — não estão na resposta final, só ilustrativos):
  • Suponha:
    • carrosel = Carrosel([‘a’,‘b’,‘c’]) # não circular
    • carrosel.item_atual -> ‘a’
    • carrosel.avancar(); carrosel.item_atual -> ‘b’
    • carrosel.avancar(); carrosel.item_atual -> ‘c’
    • carrosel.avancar(); carrosel.item_atual -> ‘c’ (permanece no fim)
  • Suponha circular:
    • carrosel = Carrosel([‘a’,‘b’,‘c’], circular=True)
    • avancar até ‘c’ e avancar de novo -> volta para ‘a’
    • retroceder em ‘a’ -> vai para ‘c’

Resposta

class Carrosel:
    def __init__(self, itens, circular=False):
        """
        itens: iterável de itens (é feita uma cópia interna como lista)
        circular: bool, se True o carrosel 'dá a volta' ao avançar/retroceder
        """
        self._itens = list(itens)
        self._circular = bool(circular)
        self._cursor = 0  # índice do item atual (0-based)

    @property
    def item_atual(self):
        """Retorna o item atual ou None se a lista estiver vazia."""
        if not self._itens:
            return None
        return self._itens[self._cursor]

    def avancar(self):
        """Avança um passo. Se no fim e circular=True, volta ao início."""
        if not self._itens:
            return
        if self._cursor < len(self._itens) - 1:
            self._cursor += 1
        else:
            if self._circular:
                self._cursor = 0
            # se não é circular, fica no fim

    def retroceder(self):
        """Retrocede um passo. Se no início e circular=True, vai para o fim."""
        if not self._itens:
            return
        if self._cursor > 0:
            self._cursor -= 1
        else:
            if self._circular:
                self._cursor = len(self._itens) - 1
            # se não é circular, fica no início

    def ir_para_inicio(self):
        """Posiciona o cursor no primeiro item (se existir)."""
        if not self._itens:
            return
        self._cursor = 0

    def ir_para_fim(self):
        """Posiciona o cursor no último item (se existir)."""
        if not self._itens:
            return
        self._cursor = len(self._itens) - 1

Exercícios relacionados