본문 바로가기
TIL (Today I Learned)/코딩테스트 연습

[programmers] 같은 숫자는 싫어

by 둥굴프 2023. 9. 6.
가독성을 위해서 문제 설명생략합니다. 링크를 통해 확인해 주세요.

 

지난 포스팅에서 자바스크립트의 Array 메서드를 알아봤습니다.

이번에는 Array 메서드를 사용하며 발생했던 오류와 그 이유에 대해서 작성해 보겠습니다.

 

예시로 들 문제는 프로그래머스 레벨 1 문제 '같은 숫자는 싫어'입니다.

 

문제 링크 : https://school.programmers.co.kr/learn/courses/30/lessons/12906

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

입력받는 매개변수는 "Array 객체"

반환해야 하는 값은 조건을 만족하는 "Array 객체"

 

reduce를 사용하여 새로운 값을 반환하면 되겠다고 생각했다.

 

1. 에러가 발생하는 코드

function solution(arr) {
    return arr
        .reduce((a,c)=> a.at(-1) === c
                ? a
                : a.push(c) ,[]);
}

초기화 값인 두 번째 매개변수는 빈 배열로 하고,

콜백에서는 누산 된 배열의 마지막 인덱스 값(a.at(-1))이랑 배열의 현재 요소(c)랑 같으면 어떤 변경도 안 한다.

만약 다르다면 현재 값을 배열에 push 한다.

 

실행하면 다음과 같은 에러가 발생한다.

TypeError 발생

에러 발생의 이유는 다음과 같다.

1. 첫 번째 요소 평가 시 [].at(-1) === c는 false이다.

2. a.push(c)를 평가한다.

3. a.push(c)는 배열 a의 맨 뒤에 요소 c를 추가하고 배열의 길이를 반환하는 메서드이다.

4. a는 [c]가 아닌, 실수 1이 된 상태로 a.at(-1)을 실행한다.

5. 실수에는 at 메서드가 없기 때문에 에러가 발생한다.

 

2. 에러가 발생하지 않는 코드

function solution(arr) {
    return arr
        .reduce((a,c)=> a.at(-1) === c
                ? a
                : (a.push(c),a) ,[]);
}

이전 코드에서 문제는 a.push(c)가 평가된 이후 배열 a를 반환하지 않아 에러를 야기했다.

다수의 평가를 거친 후 하나의 값을 반환하기 위해서 쉼표 연산자를 사용했다.

쉼표 연산자는 각각의 피연산자를 왼쪽에서 오른쪽 순서로 평가하고, 마지막 연산자의 값을 반환한다.

 

쉼표 연산자는 다음과 같은 경우에도 사용할 수 있다.

function myFunc() {
  var x = 0;

  return (x += 1), x; // ++x 와 같은 효과
}

myFunc는 지역변수 x에 할당된 값인 0에 1을 더하여 반환하는 함수이다.

만약 return x++; (x += 1과 같은 효과)이라고 작성하게 된다면 0을 반환하게 될 것이다.

그 이유는 ++ 연산자가 오른쪽에 올 경우 마지막에 평가되기 때문에, 반환을 한 이후 1을 추가하게 된다.

하지만 위와 같이 쉼표 연산자를 사용하면 ++ 연산자를 평가한 이후 x를 반환할 수 있게 된다.

(주석에 적혀있듯, ++x로 작성해도 된다.)

 

하지만, 다음과 같이 '효율성 테스트'를 통과하지 못한다.

시간 초과

3. 최적화 코드 ( 효율성 테스트 통과 코드 )

function solution(arr) {
    return arr
        .reduce((a,c)=> a[a.length - 1] === c
                ? a
                : (a.push(c),a) ,[]);
}

at 메서드가 아니라, 전통적인 js 인덱스 접근으로 바꾸니 다음과 같이 시간이 절반 이상 줄었다.

시간 절반 이상 감소

at 메서드에 대한 MDN 설명에 따르면 'index < 0인 경우, index + array.length로 접근합니다.'라고 명시되어 있다.

작동 원리는 직접 접근하는 것과 같은데, 어째서 이런 차이가 발생할까?

 

at 메서드가 추가적인 함수 호출로 인해 오버헤드가 발생한다고 생각된다.

직접 인덱스로 접근하는 것이 함수를 호출해서 접근하는 것보다 빠르기 때문이다.

 

 

 

 

글을 마치며,

어려운 문제가 아니었지만, 에러가 왜 발생하는지 모르면 답답한 문제일 수 있었다.

위처럼 문제를 풀다 보면 '반환 값'과 'side effect'의 여부로 인해 종종 실수와 오류를 야기한다.

여기서 side effect는 함수(및 메서드)가 원본 값을 변경하는지에 대한 여부이다.

 

또한, at 메서드의 문제에서 확인한 것처럼 코드의 가독성과 안전성이 중요한지 퍼포먼스가 중요한 지에 따라 어떻게 코드를 작성할지 고민하는 것도 좋은 것 같다.

 

긴 글 읽어주셔서 감사드립니다.

23.09.05

댓글