개요
이분 탐색을 학습하면서 다음과 같은 코드를 짰다.
function bs(target) {
let start = 0;
let end = numbers.length - 1;
while (start <= end) {
let mid = Math.floor((start + end) / 2);
if (numbers[mid] === target) {
return 1;
}
if (numbers[mid] > target) {
end = mid - 1;
} else {
start = mid + 1;
}
}
return 0;
}
이 코드에서는 while
루프 안에서 let mid = ...
와 같이 mid
변수를 선언하고 있는데, 문득 저렇게 해서는 안 되지 않을까? 하는 생각이 들었다. 저렇게 반복문 안에 변수 선언문이 들어가 있을 때, 어떤 일이 일어날까?
let
키워드
자바스크립트의 let
키워드는 블록 스코프를 가진다. 이 말인 즉슨, 코드 블록(if, for, while 등) 내부에서만 let
으로 선언한 변수가 유효하다는 말이다. 이렇게 말하니 왠지 거창해보이지만 사실 대부분의 프로그래밍 언어가 그렇다. for
문 안에서 선언한 변수를 for
문 밖에서 사용하지 못 하는 건 코딩을 조금이라도 해봤다면 배우지 않아도 충분히 아는 부분이다.
여기서 중요한 것은, 반복문이 한 번 돌 때마다 새로운 스코프가 생성된다는 점이다.
for (let i = 0; i < 3; i++) {
console.log(i);
}
위의 코드는 각 반복마다 i가 새로운 블록 스코프에서 선언되는 것과 동일한 효과를 가진다.
i = 0일 때, 새로운 블록 스코프가 생성되고, i = 1일 때, 또 새로운 블록스코프가 생성되고, 이렇게 각 반복마다 새로운 블록 스코프를 가진다.
while
루프 안에서 let mid
를 선언하고 있기에 매번 루프를 돌 때마다 새로운 mid
변수가 해당 블록에서 생성된다.
앞서 말했듯 let
은 블록 스코프를 갖기 때문에 현재의 while
블록에서만 유효하다.
따라서 한 번의 루프가 끝나면 기존에 선언한 mid
변수는 스코프를 벗어나게 된다.
그래서 가비지 컬렉터가 기존의 mid
를 정리하고, while
이 다시 실행되었을 때 새로운 mid
변수가 메모리에 할당된다.
기존의 mid
를 재사용하지 않는다는 말이다.
이걸 한 줄로 요약하자면, 루프를 돌 때마다 새로운 메모리 공간이 할당되고 기존의 mid
변수는 사라지는 구조다.
이건 나의 의도에 전혀 맞지 않다. 나는 mid
를 매번 새로 생성해줄 필요가 없다.
지금의 방식은 계속해서 새로운 메모리 공간을 차지하고, 루프를 돌 때마다 기존에 사용하던 mid 변수를 가비지 컬렉터가 정리해주어야 한다. 이는 성능에 악영향을 줄 수 있다.
따라서 아래와 같이 변수 선언문을 바깥으로 빼주고, 반복문 안에서는 값만 변경하는 방식이 적합하다.
function bs(target) {
let start = 0;
let end = numbers.length - 1;
let mid;
while (start <= end) {
mid = Math.floor((start + end) / 2);
if (numbers[mid] === target) {
return 1;
}
if (numbers[mid] > target) {
end = mid - 1;
} else {
start = mid + 1;
}
}
return 0;
}
아주아주 사소한 고민 하나 추가
위와 같이 mid를 초기값 없이 선언하면 mid에 새로운 값이 할당되기 전까지는 undefined 상태다. 이건 안전할까?
아마도 while문을 적어도 한 번은 돈다는 가정 하에서만 안전할 것이다. 내가 저 코드를 작성한 문제에서는 적어도 한 번 무조건 돌게 되어서 괜찮지만, 만약 while문에 들어갈 가능성이 없는 예외적인 경우를 함께 고려해야한다면? 그때는 문제가 될지도 모른다.
그렇다고 초기값을 실제 사용될 가능성이 있는 값, 이를테면 0같은 값을 넣어둘 수는 없다. 이건 코드의 명확성을 해치는 일이다.
이런 상황에서는 초기값을 어떻게 설정해두는 것이 좋을까? 코드 품질을 위해 고민해봐야 할 문제같다.
내 생각에는 이 코드에서는 mid 인덱스가 -1이 될 가능성은 영영 없기에 -1로 해놓는 게 어떨까 싶다.