강의로 돌아가기
김동현

08, 09 실습과 10-3 실습에서 self.nodeCount 변경문제

제가 실수한건지도 모르겠습니만
리스트 요소를 삭제하게 되면 당연히 self.nodeCount 도 줄어 들어야 하는데
self.nodeCount 를 체크하는데에 일관성이 없는 것 같습니다.
확인 부탁드리겠습니다

(10-3) 실습
popAt 에서 self.nodeCount -= 1 없으면 에러
popAfter 와 popBefore에 각각 self.nodeCount -= 1 를 넣고 popAt 에서 빼는게 더 정확하겠습니다만
어찌되었든 self.nodeCount 를 감소시켜야 통과합니다.

def popAt(self, pos):
        if pos >= 1 and pos <= self.nodeCount:
            prev = self.getAt(pos-1)
            self.nodeCount -= 1    # 반드시 있어야 통과
            return self.popAfter(prev)
        else:
            raise IndexError

(09) 실습
popAt 에서 self.nodeCount 변경 없이도 테스트 통과
이 경우도 popAfter 에 self.nodeCount -= 1 를 넣는게 더 정확하지만
self.nodeCount 변경없이도 테스트가 통과됩니다.

def popAt(self, pos):
        if pos >= 1 and pos <= self.nodeCount:
            prev = self.getAt(pos-1)
            # self.nodeCount -= 1    존재 유무와 상관없이 통과
            return self.popAfter(prev)
        else:
            raise IndexError

(08) 실습
이 실습은 popAt 메서드 1개뿐이라 여기서 self.nodeCount 를 변경해야하나
오히려 self.nodeCount를 변경하면 에러가 납니다.

def popAt(self, pos):
        if pos >=1 and pos <= self.nodeCount:
            if pos == 1:
                tmp = self.head.data
                self.head = self.getAt(2)
                # self.nodeCount -= 1    유무 상관없이 통과
                return tmp

            prev = self.getAt(pos-1)
            if pos == self.nodeCount:
                tmp = self.tail.data
                self.tail = prev
                # self.nodeCount -= 1    유무 상관없이 통과
                return tmp

            to_del = prev.next
            post = to_del.next
            prev.next = post
            # self.nodeCount -= 1    오히려 존재시 에러 (테스트 케이스 2), 없으면 통과 
            return to_del.data
        else:
            raise IndexError
2 개의 답변
이시윤

우선은 좋은 지적에 대해 감사드립니다.

각 연습문제의 코드는 테스트 케이스를 통해서 확인하도록 되어 있습니다. 위에 언급하신 연습문제들에서는, 각 연산의 결과 리스트의 모습이 올바르게 되어 있는지를 검사하는 부분에서, 이 리스트의 nodeCount 가 올바른 값을 가지고 있는지를 체크하지 않고 단지 리스트의 순회를 통하여 원소들이 올바르게 연결되어 있는지만을 테스트했기 때문에 self.nodeCount 값을 제대로 변화시키지 않은 경우에도 테스트 케이스를 통과하도록 되어 있었습니다.

이것은 테스트 케이스가 수정되어야 할 부분입니다. 그런데, 이미 연습문제를 통과한 학생들이 있어서 이 경우 테스트 케이스를 수정하면 수업 관리에 어떤 일이 일어나는지를 먼저 프로그래머스의 서비스 운영팀과 의논하여 케이스를 수정할지 말지를 결정해야 할 것 같습니다. 어쨌든 의미 있는 지적에 대해서 감사하게 생각합니다. 가능한 한 반영하여 연습문제의 테스트 케이스를 정비하도록 하겠습니다.

그런데, 각 연습문제에서 nodeCount 를 1 만큼 감소시키지 않는 경우 오히려 테스트를 통과하는 것은, 제출한 코드가 올바르지 않은 부분이 있기 때문인 것으로 보입니다. (물론, 테스트 케이스는 이런 것들을 모두 검사하여 판별할 수 있도록 재조정되어야 합니다만) 예를 들어, 위의 (08) 실습에서의 마지막 주석 오히려 존재시 에러, 없으면 통과 부분을 살펴보면 아래와 같습니다.

일단, 테스트 케이스의 코드는 self.nodeCount 의 값이 올바른지를 체크하지 못하고 있음은 위에서 설명한 바와 같습니다. 그런데, 이 부분이 있는 경우가 올바른 코드라 할 것입니다. 이 때, 노드의 개수가 두 개이며 그 모습은 a -> b 와 같이 연결되어 있다고 합시다. 이 때 popAt(2) 를 호출하면, pos == self.nodeCount 의 조건이 참이 되어 (마지막 노드를 삭제하려는 경우) 위 코드에서 두번째 덩어리가 실행됩니다. self.tail = prev 로 하여 tail 포인터를 조정해 준 것은 좋으나, 추가로 prev.nextNone 이 되어야 하는데 그 동작을 하는 코드가 빠져 있습니다.

이 때, 만약 self.nodeCount 가 올바른 값을 가지고 있는 경우에는 위의 이유로 인하여 특정 테스트 케이스를 통과하지 못합니다 (케이스 2번). 그러나, 만약 self.nodeCount 의 값이 올바르지 못하여 (직전 삭제에서 감소시키지 않음으로써 예를 들어 3 의 값을 가지고 있었더라면) pos == self.nodeCount 의 조건이 거짓이 되어 두번째가 아닌 세번째의 코드 덩어리가 실행되게 됩니다. 이 때는 post == to_del.next 로서 이것이 None 이므로 링크가 올바르게 조정됩니다. (단, self.tail 이 올바르게 조정되지 않는 버그가 있겠죠.)

한편, (10-3) 의 연습문제의 경우에는 popAfter()popBefore() 의 구현이 어떻게 되어 있는지를 알 수 없으므로 여기 적힌 코드만 보고 어떤 일이 벌어졌을지를 짐작하기 어렵습니다만, 아마도 위에 언급한 경우와 비슷하게, 뭔가 테스트 케이스의 사이드 이펙트로 벌어진 일일 가능성이 높아 보입니다.

이러한 부분을 모두 고려하여 테스트 케이스를 만들었어야 했으나, 그러지 못한 데 대해서는 양해를 구합니다. 위에 말씀드린 것처럼, 이러한 피드백을 고려하여 테스트 케이스를 정비하도록 하겠습니다. 다시한번 감사드립니다.

이시윤

연결 리스트 강의의 연습문제에서, 이 질문에서 얘기해 주신 것처럼 리스트가 올바로 구성되어 있는지에 대한 자세한 측면들 (예: head, tail, nodeCount 등) 을 검사하지 않고 있었던 기존 테스트 케이스를 수정하여, 제 8 강부터 제 10 강까지의 연습문제를 업데이트하였습니다.

문제는 그대로이나, 정답 여부를 체크하는 테스트 케이스만 수정되었습니다. 바꾸어 말하면 코드 채점에서 정답 통과 기준이 조금 더 까다로워지는 것과 유사합니다. 다시한번 풀어보시는 것도 좋을 것 같습니다.

테스트 케이스의 확장에 도움을 주신 데 대하여 다시한번 감사드립니다.

답변 쓰기
이 입력폼은 마크다운 문법을 지원합니다. 마크다운 가이드 를 참고하세요.