[24.06.10] TIL - 007

0. 오늘은 무얼 했는가

오늘은 조금 늦게 일어났습니다. 눈을 떠보니까 9시 20분즈음...

휴대폰을 보니까 전화가 와 있었고 지각이라는 걸 자각했습니다.

어쨌든, 빠르게 일어나서 출석체크하고, 코드 카타를 시작했습니다.

이후, 스크럼을 하고, 개인 과제에 추가해보고 싶은 내용이 있어 조금 추가하고 제출했습니다.

오후에는 개인적으로 궁금했던 내용들을 공부했습니다.

그리고, 7시부터 특강을 듣고, 스크럼 이후 하루 공부를 마무리했습니다.

1. 코드 카타

[ 09:20 ~ 10:00 ]

오늘 코드 카타 문제는 그 유명한 피보나치 수입니다.

피보나치 수는 앞선 수와 그보다 한 번 더 앞선 수를 더했을 때 지금의 수가 나오는 수열입니다.

해당 문제 링크

문제를 본 저는 바로 재귀 함수를 입력했습니다.

재귀 함수가 돌아가는 걸 지켜보던 저는 오늘 문제 쉽네 라고 생각하며 자만하고 있던 찰나...

시간 초과라는 말과 함께 실패하고 말았습니다.

당황하던 저는 어떻게 해야 할지 고민하던 중, while문을 써서 만들어보았으나, 전혀 해결되지 않았고

결국 질문하기를 봤는데...

7번부터 테스트케이스 틀리는 이유에 대한 짧은 답 이라는 제목을 보고 깜짝 놀랐습니다.

사실... 문제에는

2 이상의 n이 입력되었을 때, n번째 피보나치 수를 1234567으로 나눈 나머지를 리턴하는 함수, solution을 완성해 주세요.

라는 조건이 붙어있었고, 저는 이걸 못 보고 그냥 지나쳤었습니다...

그래서 우선 while문으로 만든 코드에 적용시켰고, 결과는 성공이었습니다.

class Solution {
    fun solution(n: Int): Int {
        var answer = 0
        var array = intArrayOf(0, 1)
        var initialize = 2
        while(initialize <= n) {
            array += (array[initialize-2] + array[initialize-1]) % 1234567
            initialize++
        }
        answer = array[n]
        return answer
    }
}

시간이 좀 걸리긴 했지만 성공했습니다...

앞으로 제한 사항 좀 잘 읽고 풀어야겠습니다.

2. 시간 복잡도

[ 14:00 ~ 15:00 ]

앞서 말한 코드 카타를 비롯해 저를 괴롭히던 문제는 항상

시간 초과

였습니다... 그래서 대체 내가 뭘 잘못했길래 시간이 항상 부족한지 고민고민했고 결국 정보처리기사 때 공부했던

시간복잡도에 대해 살짝 들여다보기로 했습니다.

근데 사실 별 내용 없었습니다.

이런 내용인데, 단순히 말해서, 제가 쓴 코드가 얼마나 시간이 걸리는지를 표현한 방법이었습니다.

일반적으로는 대문자 O를 통해 표기해서 빅-오 표기법이라고 부르는 것 같습니다.

맨 오른쪽 O(1)부터 보겠습니다. O(1)의 의미는 어떤 걸 넣든 딱 1번만 진행한다는 의미입니다.

정말 간단하죠? 그래서 좋은 시간복잡도를 갖고 있다고 볼 수 있습니다.

반면, O(n!)은 천장을 뚫고 지나, 성층권까지 갈 것 같은데요. 왜냐하면, n의 팩토리얼만큼

시간이 걸리기 때문입니다.

네... 정말 아무 내용 없습니다. 물론, 더 깊숙하게 들어가면 팔 내용들은 많고, 자연스럽게 알고리즘들도

공부하게 되겠지만, 아직은 거기까지 들어가지는 않겠습니다.

앞으로, 코드 카타 할 때 시간 복잡도를 의식하고, 이게 얼마나 돌아갈지 정도 생각은 하고 풀어야겠습니다.

3. 문자열을 다루는 객체

[ 15:00 ~ 16:00 ]

코틀린에서는 기본적으로 String이 타입입니다. 정확히 하면, String이라는 객체입니다.

코틀린은 Any라는 모든 객체의 어머니같은 존재가 있는데요. 이 객체로부터 하나씩 특징을 달고 나온 것들 중

하나가 String이 됐습니다.

이 내용을 공부하게 된 가장 큰 이유 역시, 코드 카타였습니다.

매일 아침 알고리즘 문제를 풀다 보니, 제 코딩 실력이 어느 정도인지 인지를 하게 됐고, 그에 따라, 다양한 방식으로 코딩 실력을 늘리기 위해 공부하고 있습니다...

String같은 경우는 불변 문자열 객체입니다. 그래서 변하는 쪽보다는 읽어들이는 쪽에 좀 더 강점이 있습니다.

저는 코드 카타를 하면서 몇 줄만에 끝날 수 있는 문제들을 수도 없이 긴 코드로 만들어버리는 일이 많았습니다.

특히나, 문자열을 다루면서 그런 일이 자주 벌어졌는데요. 왜냐하면, String으로 문자를 찾고, 제거하고, 설정하려고 시도했기 때문입니다.

보통 코틀린에서 문자열을 바꾸는 일을 할 땐 StringBuilder를 많이 씁니다.

StringBuilder는 문자열을 다루기 좋은 가변 객체입니다. 이 StringBuilder의 확장 함수에 대해 오늘은 공부했습니다.

val sb = StringBuilder(문자열)

먼저 StringBuilder를 선언하는 방식입니다. StringBuilder 안에는 문자열을 넣어줍니다.

값을 추가하거나, 특정 위치 값을 바꿀 수도 있습니다.

// 추가
sb.append(값)
sb += 값

// 설정
sb.set(인덱스, 값)

그리고, 깔끔하게 초기화하거나, 값을 삭제하고, 특정 범위에 있는 값을 바꿔 버릴 수도, 아니면 아예 뒤집어버릴 수도 있습니다.

// 초기화
sb.clear()

// 삭제
sb.deleteCharAt(인덱스)
sb.deleteRange(처음, 끝)

// 변경
sb.replace(처음, 끝, 값)

// 뒤집기
sb.reverse()

이렇게 유용하게 사용할 수 있습니다.

그 이외에도 많은 확장 함수가 있습니다. 근데, 단순히 StringBuilder만 쓰는 함수가 아닌 것도 있습니다.

2-1. CharSequence

방금 말씀드렸듯이, Any는 모든 객체들의 어머니같은 존재입니다.

이는 제대로 말하면, Any는 모든 객체들의 조상이라는 뜻입니다. 인간이 오스트랄로피테쿠스가 진화한 것처럼요.

즉, String, StringBuilder의 부모는 Any가 아니라, 다른 객체라는 뜻입니다.

그게 바로 CharSequence입니다. CharSequence는 읽기 전용 문자열 인터페이스로, String, StringBuilder, StringBuffer의 부모 객체입니다.

이는 저 자식 객체들이 CharSequence의 기능도 포함하고 있다는 뜻이 됩니다.

그래서 CharSequecne의 확장 함수들을 소개하겠습니다.

val cs = CharSequence()

cs.count { 조건 }

먼저, count를 통해 조건에 맞는 문자 개수를 셀 수 있습니다.

cs.contains(값)

cs.startsWith(값)
cs.endsWith(값)

또, contains로 해당 값이 존재하는지, startsWith로 해당 값으로 시작하는지, endsWith로 해당 값으로 끝나는지 확인할 수도 있습니다.

cs.substring(범위1, 범위2)

cs.isBlank()
cs.isEmpty()

substring으로 범위에 해당하는 문자열을 제거할 수도 있고, isBlank로 비어있거나 공백인지 확인하고, isEmpty로 비어있는지만 확인할 수도 있습니다.

단순히 코드 카타뿐 아니라, 일반적으로 개발할 때도 문자열은 자주 쓰는 타입이기에, 먼저 공부해야 할 것 같아, 오늘 공부하게 됐습니다.

다음엔 컬렉션들에 대해 차근차근 공부할 생각입니다.

3. 알고리즘 처음부터 다시...

[ 16:00 ~ 18:00 ]

코틀린을 다시 공부하면서 든 생각이, 알고리즘도 복습해보자 였습니다.

물론, 초반부터 진행은 하지 않았기에, 복습이 알맞은 말은 아니겠지만, 어쨌든 처음부터 풀어보고 싶었습니다.

모든 코드를 적을 순 없으니, 문제를 풀면서 배웠던 점들을 다시 복습하는 시간을 가지겠습니다.

1.10번 문제에서 평균을 구하는 문제가 있었습니다.

물론, 하나씩 반복하는 것도 방법이지만, 저는 코틀린을 믿었고, 코틀린은 저에게 답을 해주었습니다.

.average()

함수들이 다 예상 가능한 이름들을 갖고 있어, 설마하고 넣어봤더니 정말로, 저에게 평균값을 주었습니다...

2. 13번 문제에서 문자열을 숫자로 변환 후 계산하는 문제가 있었습니다.

정확히는 숫자를 입력받고, 그 숫자의 각 자릿수끼리 더하는 문제였습니다.

그래서, String으로 변환하고, for문으로 하나씩 더해줬는데...

전혀 다른 값이 나와버렸습니다. 1 + 2 + 3이라 6이어야 하는데, 150이 나와버렸습니다.

아니 이게 말이 되는 건가 싶어서 print로 출력해봤는데 1, 2, 3 대신 49, 50, 51이 들어갔습니다.

두자리의 숫자가 연달아 있는 걸 보고 느꼈습니다.

아스키 코드로 변환됐구나

저는 이전에 Char을 Int로 바꾸면 아스키 코드로 바뀐다는 걸 기억해냈고, String으로 바꾸고 Int로 다시 바꿔 해결했습니다.

3. 19번 문제에서는 제곱과 제곱근을 이용해야 했습니다.

근데, 제곱과 제곱근에 대해 생각이 나지 않았고, 검색을 통해 함수를 알아내 풀었습니다.

import kotlin.math.*

// 제곱근
sqrt(double값)

// 제곱
dobule값.pow(지수)

매우 간단한 함수니 다시 머리에 박아넣어야겠습니다.

4. 20번 문제는 정수를 나열하는 문제였습니다.

저는 매우 복잡하게 썼지만, 문제 풀이를 보니 1줄로도 풀 수 있었습니다.

toCharArray()로 String을 CharArray로 변환하고, 그걸 sortedArrayDescending으로 내림차순 정렬했습니다.

사실, 이 문제는 그냥 넘길까 생각했지만, 제 자존심보다 중요한 건 그걸 뒷받침해줄 실력이라 생각하고,

제 실력을 늘리기 위해 다른 사람들의 풀이 방식을 참고했다는 걸 알립니다.

배움은 나쁜 것이 아니라고 생각합니다. 배우고 이해하지 못해도 나쁘지 않다고 생각합니다.

대신, 이해하지 못했는데 배우는 걸 멈추면 나쁜 결과를 가져온다 생각합니다.

자존심을 버리고 항상 배우는 자세를 가져야겠다는 생각을 했습니다.

4. 끝

사실 19:00 ~ 20:00 까지 특강을 진행했었습니다.

이를 따로 다루기보다는 끝과 함께 작성하고 싶어, 따로 작성하지는 않겠습니다.

우선, 저는 개인 과제를 어느 정도 만족하며 마무리했었습니다.

19시에 강의를 듣고, 저는 좋은 코드가 무엇인지에 대해 다시 생각해보게 되는 계기가 되었습니다.

저 스스로 좋은 코드가 깔끔한 코드 정도로 생각했었는데, 튜터님은 이렇게 말씀하셨습니다.

남들이 잘 이해할 수 있는 코드

그리고 저는 깨달았습니다. 결국, 사람들 사이 일하는 거고, 사람들끼리 말할 때는 C언어나 파이썬, 코틀린이 아닌,

한국어, 영어와 같은 언어로 말한다는 걸요.

결국 남들과 협업한다는 건 잘 소통하고, 서로를 이해한다는 것인데, 남들이 이해하기 어려운 코드를 짜놓으면

그게 진짜 좋은 코드가 맞을까?

이에 대해 생각해볼 수 있어서 좋았습니다.

끝.

profile
여기는 공부 기록용 블로그

0개의 댓글