First, remember that using recursion nay is the best way to solve this problem. A simple loop by the list would solve. Anyway...
One way to solve is to make the function receive the index, as you indicated another answer (I would only change it to check if the index is at the edge of the list, instead of letting the IndexError
). But if you want to follow your idea and not use the index as a parameter, the solution would be:
- check whether the first item on the list should be exchanged (and exchange it, if applicable)
- concatenate the first element with the swap result in the rest of the list
That is to say:
def troca_recursiva(lista, tirar, colocar):
if not lista or tirar == colocar: # se a lista é vazia ou elementos são iguais, retorna ela mesma
return lista
# verifica se deve trocar o primeiro elemento
primeiro = lista[0]
if primeiro == tirar:
primeiro = colocar
# retorna lista com o primeiro elemento, concatenada com a troca do restante
return [primeiro] + troca_recursiva(lista[1:], tirar, colocar)
First I check if the list is empty (if not lista
, since empty lists are considered False
), and if it is, return the list itself, because there is no reason to continue. And I also included a small improvement, which is to check if the old and new values are equal (in this case, it also doesn’t make sense to have all the work of exchanging, just return to the list itself, without modification).
Then I check if the first element should be exchanged (and change, if applicable). Then just create a list with the first element, and concatenate with the result of changing the rest of the list: lista[1:]
creates a sub-list containing the second element onwards (or an empty list if there are no more elements).
This method is extremely inefficient because it creates multiple sub-lists. Also, many recursive calls can cause a pile burst (even though you can change the recursion limit with sys.setrecursionlimit
, is still inefficient because of the creation of sub-lists).
Attention to a detail. I could to have made the exchange of the first element like this:
if lista[0] == tirar:
lista[0] = colocar
Only that would modify the original list. Let’s see the difference:
def troca_recursiva_nao_muda_lista(lista, tirar, colocar):
if not lista or tirar == colocar:
return lista
primeiro = lista[0]
if primeiro == tirar:
primeiro = colocar
return [primeiro] + troca_recursiva_nao_muda_lista(lista[1:], tirar, colocar)
def troca_recursiva_muda_lista(lista, tirar, colocar):
if not lista or tirar == colocar:
return lista
if lista[0] == tirar:
lista[0] = colocar
return lista[:1] + troca_recursiva_muda_lista(lista[1:], tirar, colocar)
lista = [0, 1, 2, 0]
print(troca_recursiva_nao_muda_lista(lista, 0, 7)) # [7, 1, 2, 7]
print(lista) # [0, 1, 2, 0] <- lista original intacta
print(troca_recursiva_muda_lista(lista, 0, 7)) # [7, 1, 2, 7]
print(lista) # [7, 1, 2, 0] <- somente o primeiro foi mudado
Note that using the first version, the original list is not modified. Already using the second version, the list is modified, but only the first element. This is because from the second element we are dealing with sub-lists created internally, and that no longer affect the original list. Still, you have to check whether or not you want to change the original list (the other answer "solves" this by creating a copy of the list with the method copy
, But let’s face it, it’s the function that should take care of that detail, not who’s calling it - and if whoever calls the function forgets to pass the copy? The function should make it clear whether or not it modifies the list, and as we have seen, it is not so difficult to guarantee this).
Just for the record, see how much easier it would be without recursion (and with the advantage of not creating multiple sub-lists, nor having the risk of popping the stack):
def troca_muda_lista(lista, tirar, colocar):
for i, n in enumerate(lista):
if n == tirar:
lista[i] = colocar
# não preciso retornar porque modifiquei a própria lista
def troca_nao_muda_lista(lista, tirar, colocar):
# retorna outra lista com os elementos modificados
return [ colocar if n == tirar else n for n in lista ]
lista = [0, 1, 2, 0]
print(troca_nao_muda_lista(lista, 0, 7)) # [7, 1, 2, 7]
print(lista) # [0, 1, 2, 0] <- lista original intacta
troca_muda_lista(lista, 0, 7)
print(lista) # [7, 1, 2, 7] <- a lista foi modificada
The function that modifies the list is not returning anything because it doesn’t make much sense, since it modifies the list itself (it would be redundant to return the list itself, since it has already been modified within the function).
This is not recursion. Recursion is to iterate individually for each element of the list by following a criterion. When you do
if not old in values:
andindex = values.index(old)
you every iteration of the function you repeat twice for input increasing the complexity of the algorithm. It is quite wrong.– Augusto Vasques
You’re right, I hadn’t thought of it. I corrected the answer, you can take a look ?
– JeanExtreme002
ISSOOOO!!! It’s just right.
– Augusto Vasques
Hey Augusto if you have time, you can go there in chat and try to help me with a problem of incompatibility between some programs I use and the . NET Framework 4.8 ?
– JeanExtreme002
Then, now I’m watching Black Cover. You can explore this index in a way that you don’t have to deal with an exception to get out of recursion.
– Augusto Vasques
Okay, I already see how I improve the code because I’m going to watch anime now. And by the way, I didn’t know that you were Otaku or that you like to see anime tbm kk
– JeanExtreme002
now I understand my old beautiful explanation.
– Romulo Silva Souza Santos
@Jeanextreme002 I don’t know if you’ve seen it, but I left an answer there in chat.
– Augusto Vasques