파이썬에서 리스트를 다룰 때 흔히 마주치는 문제 중 하나는 리스트를 순회하면서 동시에 요소를 제거하는 상황입니다. 이런 작업이 예상치 못한 결과를 낳는 경우가 많은데, 오늘은 이 문제와 그 해결 방법을 알아보겠습니다.
문제가 있는 코드
다음 코드를 봐주세요:
def remove_max_values(values):
if not values: return
max_val = max(values)
for i, v in enumerate(values):
if v == max_val:
values.pop(i)
result.append(v)
return result
# 테스트
values = [5, 5, 4, 3, 5, 2]
result = []
print("원래 리스트:", values)
remove_max_values(values)
print("결과:", result)
print("남은 값들:", values)
이 함수는 리스트에서 최대값들을 찾아 제거하고 다른 리스트에 추가하려 합니다. 하지만 이 코드를 실행하면 예상과 다른 결과가 나옵니다:
원래 리스트: [5, 5, 4, 3, 5, 2]
결과: [5, 5]
남은 값들: [5, 4, 3, 2]
예상과 달리 모든 5가 제거되지 않았습니다.
왜 이런 일이 일어날까요?
이 문제는 리스트를 순회하면서 동시에 요소를 제거하기 때문에 발생합니다. pop() 메소드로 요소를 제거하면 리스트 크기가 변하고, 이는 반복 중에 예상치 못한 결과를 낳습니다.
예를 들어, [5, 5, 4, 3] 리스트를 순회한다고 가정해 봅시다:
첫 번째 5를 제거합니다.
리스트는 [5, 4, 3]이 됩니다.
인덱스가 1로 증가하여 4를 가리킵니다.
두 번째 5를 건너뛰게 됩니다.
반응형
어떻게 고칠 수 있을까요?
이 문제를 해결하기 위한 몇 가지 방법이 있습니다:
리스트를 거꾸로 순회합니다.
제거할 인덱스를 따로 저장한 후 한 번에 제거합니다.
리스트 컴프리헨션으로 새 리스트를 만듭니다.
해결책
이 문제를 해결하기 위해 다음과 같이 코드를 수정할 수 있습니다:
def remove_max_values(values):
if not values:
return []
max_val = max(values)
to_remove = []
for i, v in enumerate(values):
if v == max_val:
to_remove.append(i)
result = []
# 큰 인덱스부터 제거하여 앞의 인덱스에 영향을 주지 않도록 함
for i in sorted(to_remove, reverse=True):
result.append(values.pop(i))
return result
# 테스트
values = [5, 5, 4, 3, 5, 2]
print("원래 리스트:", values)
result = remove_max_values(values)
print("결과:", result)
print("남은 값들:", values)
이 수정된 코드를 실행하면 다음과 같은 결과가 나옵니다:
원래 리스트: [5, 5, 4, 3, 5, 2]
결과: [5, 5, 5]
남은 값들: [4, 3, 2]
이제 모든 최대값(5)이 올바르게 제거되고 결과 리스트에 추가되었습니다.
설명
이 해결책이 작동하는 이유는 다음과 같습니다:
먼저 모든 최대값의 인덱스를 찾아 to_remove 리스트에 저장합니다.
그 다음, 이 인덱스들을 큰 순서대로 정렬합니다.
큰 인덱스부터 요소를 제거하면, 앞쪽 인덱스의 요소들이 영향을 받지 않습니다.
이 방식을 사용하면 리스트를 한 번만 순회하면서도 모든 최대값을 정확하게 찾아 제거할 수 있습니다.
결론
리스트를 순회하면서 요소를 제거할 때는 항상 주의가 필요합니다. 이런 상황에서는 리스트의 크기가 변경되어 예상치 못한 결과가 발생할 수 있기 때문입니다. 위에서 제시한 방법을 활용하면 이러한 문제를 피하고 원하는 결과를 얻을 수 있습니다.
파이썬으로 알고리즘을 짜실때 이러한 점을 기억하시면 더욱 안정적이고 예측 가능한 코드를 작성하실 수 있을 것입니다.