falsy
값이란 Boolean
문맥에서 false
로 평가되는 값이다.
falsy
값은 8가지가 있다.
false
0
: 숫자 0-0
: 음수 00n
: BigInt 0""
: 빈 stringnull
: 아무런 값도 없음undefined
NaN
: 숫자가 아님
falsy
값에 대해서 알고는 있었는데, 실제로 주의를 기울여야한다는 것은 코딩테스트를 연습하면서 체감하게 되었다.
다음은 2021년 카카오 채용연계형 인턴십 문제이다.
당시 코테 후기를 찾아보면 이 문제가 코테부문 당락을 갈랐다는 것을 알 수 있다.
(이 문제가 3번째 문제인데 4,5번째 문제는 각각 Lv.4, Lv.5 이다.)
처음엔 단순한 리스트로 문제를 풀었다가 효율성 부문에서 통과가 안되어서 리스트 내에 객체를 넣어서 양방향 리스트와 유사하게 작동하게끔 구현하였다.
const list = [];
for (let i = 0; i < n; i++) {
if (i == 0)
list[i] = {prev : null, next : i + 1, exist: true};
else if (i === n - 1)
list[i] = {prev : i - 1, next : null, exist: true};
else
list[i] = {prev : i - 1, next : i + 1, exist: true};
}
다음은 입력으로 들어오는 명령 각각에 대해서 수행하는 로직이다.
const list = [];
const deleted = [];
let cursor = k;
...
cmd.forEach((command) => {
const [order, num] = command.split(' ');
if (order === 'U') {
let t = parseInt(num);
while (t--) {
cursor = list[cursor].prev;
}
} else if (order === 'D') {
let t = parseInt(num);
while (t--) {
cursor = list[cursor].next;
}
} else if (order === 'C') {
const {prev, next} = list[cursor];
// 연결리스트 삭제와 유사 (실제로 배열에서 사라지지는 않음)
if (prev) list[prev].next = next;
if (next) list[next].prev = prev;
list[cursor].exist = false;
deleted.push(cursor);
cursor = next === null ? prev : next;
} else if (order === 'Z') {
const repaired = deleted.pop();
const {prev, next} = list[repaired];
// 삭제 전으로 돌아가게 만듬
if (prev) list[prev].next = repaired;
if (next) list[next].prev = repaired;
list[repaired].exist = true;
}
})
이 로직은 잘 돌아가는 것처럼 보이지만 테스트케이스 28번, 효율성케이스 8번에서 런타임 에러가 난다.
며칠을 고민하다가 친구(@dha)의 도움으로 찾을 수 있었는데,
...
if (prev) list[prev].next = next; // prev에 0이 들어올 수 있음
...
if (prev) list[prev].next = repaired;
...
null
을 방지하기 위해 넣은 조건문에 0이 들어오는 경우 조건문을 통과하지 못하기 때문이었다.
(prev, next에 저장되는 것은 인덱스이기 때문에 0이 들어올 수 있다.)
참으로 어이없는 실수다...
올바르게 고친 코드는 다음과 같다.
const list = [];
const deleted = [];
let cursor = k;
...
cmd.forEach((command) => {
const [order, num] = command.split(' ');
if (order === 'U') {
let t = parseInt(num);
while (t--) {
cursor = list[cursor].prev;
}
} else if (order === 'D') {
let t = parseInt(num);
while (t--) {
cursor = list[cursor].next;
}
} else if (order === 'C') {
const {prev, next} = list[cursor];
if (prev !== null) list[prev].next = next; // null 검사
if (next !== null) list[next].prev = prev; // null 검사
list[cursor].exist = false;
deleted.push(cursor);
cursor = next === null ? prev : next;
} else if (order === 'Z') {
const repaired = deleted.pop();
const {prev, next} = list[repaired];
if (prev !== null) list[prev].next = repaired; // null 검사
if (next !== null) list[next].prev = repaired; // null 검사
list[repaired].exist = true;
}
})
fasly
값에 대해 처음 알게되었을때 단순하게 if 문을 쓸 때 주의해야지~ 라고만 생각하고 넘어갔었는데 이번 기회로 생각을 바꾸게 되었다.
단순히 코테를 연습할 때도 잘못 쓴 코드 하나를 잡느라 며칠이 걸렸는데, 실제 코딩테스트였다면? falsy
체크 제대로 못한 것 때문에 로직을 다 생각해놓고도 떨어졌을 것이다. 내가 언제든 바보같은 실수를 할 수 있다는 것을 인정하고 if 문을 쓸 때 정확하게 명시하는 습관을 가져야겠다.
실수를 인정하고 실수 하지 않는 습관으로 발전하려는 모습이 멋있습니다~!!
덕분에 리스트를 통해 문제를 해결하는 방식도 배웠습니다.
자주 들를게요~!! :D