-
항해 99 5기 TIL_6항해 99 2022. 1. 15. 12:37
▶ Today I Learned
<알고리즘 기초>
[음양 더하기]
https://programmers.co.kr/learn/courses/30/lessons/76501
소요시간: 1시간 1분
function solution(absolutes, signs) { let totalValue = '' let answer = 0 // 배열의 값들을 어떻게 다루는 건 기본적으로 반복문을 사용함 for (let i = 0; i < signs.length; i++) { let sign = (signs[i] === true ? '+' : '-') // 부호를 어떻게 대입하지?? 부호는 일단 Number타입인듯 // 하지만 삼항 연산자에는 곧바로 쓸 수 없음, 일단 문자열로 전환 totalValue = sign + absolutes[i].toString() // totalValue는 데이터 타입이 문자열인 상황이기에 이를 정수로 전환 totalValue = Number(totalValue) answer = answer + totalValue // console.log(Number('-7')) 는 -7이 출력됨 // console.log(Number('-')) 만 출력하는 건 NaN } return answer }
한 가지 의아한 건 반복문 안에서 쌓이게 된 answer의 값은 어디까지나 반복문 안에서만 적용될 거라 생각했는데,
저렇게 return answer를 반복문 바로 바깥에 두니 반복문 안의 answer가 출력되었다. 자바에서는 안되었던 것 같은데,
확인해볼 필요가 있겠다.
=> 이에 대해 팀원 분들에게 질문하였더니 한 분께서 반복문을 풀어서 생각해보라고 하셨다.
예를 들면
i 에 1을 대입하고 연산 연산 ... 첫 번째 answer 완료
i 에 2를 대입하고 연산 연산.... 두번 째 answer 완료
...
마지막에 answer가 나옴, 그리고 바로 맞닥뜨리는 것은 return answer!
코드는 기본적으로 위에서 아래로 가면서 동작하므로 당연히 위에서 연산이 모두 끝난 answer의 값이 출력될 것!
이렇게 보니 정말 쉬웠다! 질문을 통해 한층 더 성장한 느낌이다!
설명 잘해주시는 우리 팀원 분들에게 오늘도 감사를..!(감격)
프로그래머스 모범답안
function solution(absolutes, signs) { return absolutes.reduce((acc, val, i) => acc + (val * (signs[i] ? 1 : -1)), 0); }
reduce()는
reduce(callback(accumulator, currentValue, currentIndex, array) /* arr.reduce(callback[, initialValue]) 처럼 뒤에 초기값이 올 수도 있다. 없다면 자동으로 acc(축적값)은 원본 배열 array의 처음 값 */
와 같은 형태로 이루어져 있다. 이때 반환값은 누적 계산의 결과값이다. 자세한 설명은 하단의 참조 링크 중 Mozilla 재단의
공식 문서를 참조하면 된다.
또 위에서 =>는 화살표 함수라고 한다. 쉽게 말해 좌측 값을 인자로 하는 함수를 우측에 표현해낸다.
여기서 마지막 acc는 currentValue, 즉 배열의 마지막 값 직전까지 모두 더한 값이다.
그래서 주로 acc + cur을 하면 배열의 모든 값의 합을 구하는 식이 된다.
위 식은 i라는 인자, 즉 초기값에 0을 줌으로써 acc의 시작이 배열의 첫 숫자가 아닌 0이 된다. 즉 val(cur)가 첫 숫자부터
시작하므로 sign의 여부가 True인지 False인지 여부에 따라 양수/음수가 결정되고 배열이 끝날 때 까지 호출되면서
결국 모든 양수와 음수 값을 더한 것이 출력된다.
자바스크립트의 문법과 함수들에 대해서 잘 알고 있는 사람이 생각해낼 수 있는 코드라는 생각이 든다.
언어 공부를 게을리 하지 않으면서 효율적인 코드를 짜는 연습이 필요할 것 같다.
[없는 숫자 더하기]
https://programmers.co.kr/learn/courses/30/lessons/86051
소요시간: 10분 42초
function solution(numbers) { let total = 0 for (let i = 0; i < numbers.length ; i++) { total = total + numbers[i] // 배열값 누적하여 더하기 } return ((0+9)*10/2-total) // 가우스 덧셈 활용하여 총합 빼기 // 배열 안의 수들을 더한 다음 0 ~ 9까지를 더한 수에서 빼기 = 배열 안에 없는 숫자들의 합 }
이것을 방금 배운 reduce()를 활용해 리팩토링 해보자면
function solution(numbers) { let val = 1 return 45 - numbers.reduce((acc,cur,i) => acc + val*numbers[i],0) }
하지만 아직 reduce의 구조상, 어떻게 저 i가 initial value가 되었는지 모르겠다..
reduce와 화살표 함수에 대해서 좀 더 공부하고 사용해보면 알 수 있을 것 같다!
프로그래머스 모범답안
function solution(numbers) { return 45 - numbers.reduce((cur, acc) => cur + acc, 0); }
구조가 조금 헷갈리기는 하다 ㅇㅅㅇ 변수명 순서는 바뀐 듯 하다.
[핸드폰 번호 맨 뒤 4자리 *로 가리기]
https://programmers.co.kr/learn/courses/30/lessons/12948
소요시간: 37분 52초
제출한 답안
function solution(phone_number) { let string = phone_number.substr(0,phone_number.length - phone_number.slice(-4,phone_number.length+1).length) <!-- 핸드폰 번호는 경우에 따라 숫자의 갯수가 바뀔 수 있다. 그래서 맨 뒤 번호 4자리를 자르는 것이 필요했다. 여기선 문자열의 특정 부분을 자르는 slice라는 함수를 사용했다. slice에서 앞부분은 시작 인덱스이다. 시작 인덱스를 음수로 줄 경우 뒤에서 부터 문자열을 출력한다. let i = 0; let stars = '' for (i;i<string.length; i++) { stars = stars + '*' } return stars+phone_number.slice(-4,phone_number.length) }
분명 연산하는 과정에서는 이게 맞다고 생각했다. 하지만 다시 자세히 보니 코드가 지저분하고 이해가 잘 되지 않는다.
slice를 깔끔히 정리한 답안
function solution(phone_number) { let string = phone_number.substr(0,phone_number.length - phone_number.slice(-4).length) let i = 0; let stars = '' for (i;i<string.length; i++) { stars = stars + '*' } return stars+phone_number.slice(-4) } // 깔끔하게 정리하면 이렇게 될 것이다.
cf) slice에서 음수와 양수를 조합해 사용하기도 한다. 가령
arr = ['개', '고양이', 자동차', '버섯']
이라면
console.log(arr.slice(1,-1)) 이라고 했을 때 ['고양이', '자동차'] 가 출력된다. (시작인덱스부터 끝 인덱스의 전 인덱스까지 출력)
[배열 값들의 평균 구하기]
https://programmers.co.kr/learn/courses/30/lessons/12944
소요시간: 3분 42초
function solution(arr) { let total = arr.reduce((acc,cur) => acc + cur, 0) return total/arr.length }
[가로길이 n, 세로길이 m인 * 직사각형 출력]
https://programmers.co.kr/learn/courses/30/lessons/12969
소요시간: 21분 47초
처음 작성한 코드, 로그를 찍어본 결과 저렇게 되면 내부 for loop에서 j가 초기화 되고 str은 별이 3개가 추가되어 나오는 것이 확인 되었다.
출력하면 사다리 꼴이 나온다.
두 번째로 작성한 코드
원했던 결과물이 정상적으로 나온다.
프로그래머스 모범답변
process.stdin.setEncoding('utf8'); process.stdin.on('data', data => { const n = data.split(" "); const a = Number(n[0]), b = Number(n[1]); const row = '*'.repeat(a) for(let i =0; i < b; i++){ console.log(row) } });
윗부분은 출력을 위해 짜둔 코드 같아서 생략하였다.
중요한 것은 const row 부터!
여기서 가로는 a, 세로는 b인데
repeat()은 str.repeat(반복할 횟수)의 형태로 사용해 문자열을 반복할 횟수만큼 이어붙인 값을 반환해주는 함수라고 한다.
따라서 위와 같이 가로 한줄 작성 후 그것을 세로 길이만큼 반복문을 써주는 코드를 짤 수도 있다.
repeat을 두번 사용하면 이런 코드도 가능하다.
const star = `${'*'.repeat(a)}\n`; console.log(star.repeat(b));
es6의 백틱과 표현식에 관한 설명은 해당 블로그 참조!
https://curryyou.tistory.com/185
[문자열을 정수로 바꾸기]
https://programmers.co.kr/learn/courses/30/lessons/12925
소요시간: 38초
function solution(s) { let answer = Number(s) return answer; } /* 프로그래머스 모범답안 */ function strToInt(str){ return str/1 } console.log(strToInt("-1234"));
솔직히 쉬워서 따로 해설은 안해도 되겠다 했는데 설마설마하니 이런 코드까지도 리팩토링 할 수 있을 줄은 몰랐다...
게다가 Number()라는 함수도 없이, 기본 기능으로 구현했으면서도 동적언어의 핵심을 파고 든 방법이다..!
문자열이었지만 /1을 함으로써 자동으로 숫자로 변환되는 것을 보니 그저 감탄스럽기만 하다..
자바스크립트에 대해서 잘 이해한다면 저런 것도 가능할 것 같다... 존경스럽다..
[행렬의 덧셈]
https://programmers.co.kr/learn/courses/30/lessons/12950
소요시간: 5시간 이상
let arr1 = [[1,2],[3,4],[5,6]] let arr2 = [[1,2],[3,4],[5,6]] let addedArr = [[]] // 값을 넣어줄 행렬 변수 선언문 for (let i = 0; i < arr1.length ; i++) { for (let j = 0; j < arr1[i].length ; j++) { addedArr[i][j] = arr1[i][j] + arr2[i][j] console.log(addedArr[i][j]) } }
TypeError: Cannot set property '0' of undefined가 나타난다. undefined의 프로퍼티 0을 set 할 수 없다.
0이 들어간건 변수 선언문이 쓰인 구간인데... 라며 생각하던 중
아 혹시 let j = 0은 let으로 선언한 것이라 중복선언이 안되는 특징 때문에 0을 대입(set, 혹은 설정)해 줄 수 없다는 걸까?
하고 let을 중복선언이 가능한 var로 바꾸었다.
//행과 열의 길이 let arr1 = [[1,2],[3,4],[5,6]] let arr2 = [[1,2],[3,4],[5,6]] let addedArr = [[]] for (let i = 0; i < arr1.length ; i++) { for (var j = 0 ; j < arr1[i].length ; j++) { addedArr[i][j] = arr1[i][j] + arr2[i][j] console.log(addedArr[i][j]) } } // 왜 안되는 걸까??
TypeError: Cannot set property '0' of undefined
왜 0을 설정할 수 없다는 거지..
몇 시간을 고민해도 해결이 되지 않아 문제 해결을 위해 이번엔 console.log에 입력하며 한 구간 한 구간을 따져보기로 했다.
그랬더니 답을 발견했다.
-addedArr[i] = []가 있는 경우,
처음 선언했던 let addedArr = [[]] 가 반복문을 한번 돌고 i가 1로 바뀐 후 내려올 때
[[2,4] + [] ] 와 같이 대괄호가 미리 하나 추가되는 것이다. 그 상태로 하단의 반복문을 돌게되면
값이 차례대로 들어가 원하는 결과를 얻을 수 있다. 왜일까?
그 이유는 자바스크립트에서 2차원 배열(배열안에 배열이 있는 형태)을 원시적인 방법
(배열에 존재하지 않는 자리에 값을 하나 집어넣는 방식)으로 한번에 만들 수는 없기 때문이다.
let arr1 = [[1,2],[3,4],[5,6]] let arr2 = [[1,2],[3,4],[5,6]] let addedArr = [[]] for (let i = 0; i < arr1.length ; i++) { // addedArr[i] = []이 있다면 //[ [], + [] ] 대괄호 구간 나누기 용 // i = 1, j = 2 for (var j = 0 ; j < arr1[i].length ; j++) { // 2차 i = 1, j = 0 addedArr[i][j] = arr1[i][j] + arr2[i][j] // 우측 배열 변수들 2개는 숫자로 치환됨 // Cannot set property '0' of undefined //라는 말이 addedArr[1]다음에 [j]에 0을 대입하려는게 안되는 것 같음, 즉, 자바스크립트의 배열은 동적배열이지만 동시에 두번 배열생성은 안된다? // [[2,4],6]까진 되는 데 [[2,4][6, ]]이런식은 안된다? // addedArr[0][0] = 2 // addedArr[0][1] = 4 // 2차 // addedArr[1][0] = 6 // addedArr[1][1] = 8 // console.log(addedArr[i][j]) } //i = 0, j = 2 // console.log(addedArr[i][j]) //i = 1, j = 2 // addedArr[0][2]에는 할당된 값이 없음!undefined // 1차 addedArr = [[2,4]] // addedArr[1][2]에는 할당된 값이 없음!undefined // 2차 addedArr = [[2,4], [6,8]] 자바 스크립트는 동적배열, 배열을 초과한 데이터를 넣으면 배열이 // 그만큼 커지지...않..나? console.log(addedArr) } // console.log(addedArr)
아래처럼 자바스크립트의 1차원 배열에서는 추가해도 얼마든지 늘어난다. (다른 언어는 제대로 해보지 않았지만 자바를 했었을 때는 초반에 배열의 크기를 설정해버리면 초과 시 동적으로 늘어나지 않았던 것 같다.)
let test = [1,2,3] let t = 0 let val = [] for (t; t < test.length ; t++) { val[t] = test[t] } console.log(val) //콘솔에 [1,2,3]이 찍히며 val(0) ~ val(2)까지 출력이 가능하다.
이렇게 5시간 이상을 삽질하고 고민하고 구글링하고 오류 메시지를 읽고 구간별로 콘솔을 찍은 결과 위 사실을 알게되었다.
자바스크립트의 배열에서는 2차원 배열을 단순한 방식으로 한 번에 할 수 없다는 걸!
오랜 시간 붙잡았지만 집요하게 물고 늘어져 분석한 결과 문제를 해결하게 되어 매우 뿌듯하다 ㅎㅎ
function solution(arr1, arr2) { let addedArr = [[]] for (let i = 0; i < arr1.length ; i++) { addedArr[i] = [] for (var j = 0 ; j < arr1[i].length ; j++) { addedArr[i][j] = arr1[i][j] + arr2[i][j] } } return addedArr } // 깔끔하게 정리한 코드 :) 통과!!! 유후~!
▶ 느낀 점
위에서 가장 많이 고민한 행렬의 덧셈 부분에서, 듣기론 map이라는 함수를 쓰면 더 짧은 코드로 구현할 수 있다지만
바로 그 기능부터 배우고 싶지는 않다.
가장 많이 쓰이고 기본이 되는 반복문과 조건문의 구문으로도 원하는 것을 구현할 줄 알아야 한다고 생각한다.
그래야 사고력의 기반이 다져질 것이고 추후에 고급 기능들도 어떻게 돌아가는 지 더 잘 이해할 수 있을 것 같다.
나는 오늘 고생한 덕분에 자바스크립트의 속성에 대해서 좀 더 알게 되었으며 고민하며 사고력을 향상시켰다고 생각한다.
오늘 하루도 고생많았다. 내 자신, 화이팅! :)
▶ 공부 시 참고 링크들
백틱을 이용한 템플릿 리터럴(Template literals)
- 문자열의 줄바꿈을 이스케이프 문자(\n)이 아닌 엔터로도 구현할 수 있게 해준다.
글쓸 때 처럼 말이다.
https://www.delftstack.com/ko/howto/javascript/convert-string-to-number-javascript/
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
https://ponyozzang.tistory.com/400
https://im-developer.tistory.com/103
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/String/repeat
'항해 99' 카테고리의 다른 글
항해 99 5기 WIL_1 (0) 2022.01.16 항해 99 5기 TIL_7 (0) 2022.01.16 항해 99 5기 TIL_5 (0) 2022.01.14 항해 99 5기 TIL_4 (0) 2022.01.14 항해 99 5기 TIL_3 (0) 2022.01.13