본문 바로가기
♞ | 공부일지

[Android, 내일배움캠프] 공부일지(2024-06-11)

by immgga 2024. 6. 11.
오늘 공부한 내용 정리(2024년 6월 11일)

 

 

1. 코드카타 문제풀이

A. 행사장 대여(Small)(백준, S5, 14732번)

문제 내용

 

문제 풀이 방법

  • 직사각형의 개수(N)와 직사각형의 시작점과 끝점이 주어질 때, 직사각형의 넓이를 출력.
  • 가장 큰 직사각형의 넓이를 구하는 문제가 아닌, 모든 직사각형의 합집합 넓이를 구하는 것이다.

  • 위와 같은 경우로 주어질 경우, 큰 직사각형  옆에 튀어나와 있는 점 E, H의 넓이도 같이 계산해야 한다는 뜻이다.
    넓이는 24 + 2 = 28이다.

 

해결 코드(스포 주의)

더보기
import java.util.Scanner

fun main() = with(Scanner(System.`in`)) {
    val squareCnt = nextInt()

    val coordinate = MutableList(501) { MutableList(501) { false } }
    for (i in 0 until squareCnt) {
        val squareStartX = nextInt()
        val squareStartY = nextInt()
        val squareEndX = nextInt()
        val squareEndY = nextInt()

        for (j in squareStartY+1 .. squareEndY) {
            for (k in squareStartX+1 .. squareEndX) {
                coordinate[j][k] = true
            }
        }
    }

    var cnt = 0
    for (i in 0 until coordinate.size) {
        for (j in 0 until coordinate[i].size) {
            if (coordinate[i][j]) cnt++
        }
    }
    println(cnt)
}

 

풀이 과정

  • 넓이를 구할 크기가 501 x 501의 리스트를 생성한다. 초기값은 전체 다 false이다.
  • squareCnt만큼 반복해 총 2개의 점(시작점, 끝점)을 입력받는다.
  • 그 점에 해당하는 범위만큼 2중 반복문을 돌려서 그 범위에 해당하는 데이터들만 true로 변경한다.
    이를 반복하고 나면 총 squareCnt개의 직사각형에 해당하는 좌표에 true로 채워져 있을 것이다.
  • 이를 다시 2중 반복문을 돌려서 coordinate의 데이터가 true인 경우에만 cnt를 늘려간다. 그러면 최종적인 cnt가 나오는데, 이 값이 넓이가 된다.

 

문제 해결 과정

  • 처음에 문제 이해를 잘못해 가장 큰 직사각형의 넓이를 구하는 줄 알고 풀이를 찾아보고 합집합 넓이를 구해야 한다는 것을 알게 되었다.
  • 또한 범위도 500부터 500까지 매우 작아서 false로 초기화한 2중 리스트로 구성해서 더 쉽게 해결했다. 범위가 더 커진다면 이 방법은 사용할 수 없을 것이다.
  • 문제 이해만 한다면 매우 쉽게 풀 수 있는 문제이다.

 

B. 단어 시계(백준, S5, 17091번)

문제 내용

 

문제 풀이 방법

  • 사진에 정의된 조건에 맞게 입력받은 시간을 영어로 변환해 출력.

 

해결 코드(스포 주의)

더보기
import java.util.*

fun main() = with(Scanner(System.`in`)) {
    val hour = nextInt()
    val minutes = nextInt()
    var isOverHalf = ""     // 30분 이전과 이후를 체크한다(past, to), 0인 경우는 o' clock.
    var changeMinute = ""      // 분을 영어로 변환.

    when (minutes) {
        in 1..30 -> {
            val hourEng = changeEng(hour)
            val minEng = if (minutes == 1) "minute" else "minutes"
            isOverHalf = "$minEng past"
            changeMinute = changeEng(minutes)

            if (minutes == 15 || minutes == 30) println("$changeMinute past $hourEng")
            else println("$changeMinute $isOverHalf $hourEng")
        }
        in 31 .. 59 -> {
            val hourEng = if (hour == 12) "one" else changeEng(hour+1)
            val minEng = if (60 - minutes == 1) "minute" else "minutes"
            isOverHalf = "$minEng to"
            changeMinute = changeEng(60 - minutes)

            if (60 - minutes == 15) println("$changeMinute to $hourEng")
            else println("$changeMinute $isOverHalf $hourEng")
        }
        else -> { // 정각인 경우
            val hourEng = changeEng(hour)
            isOverHalf = "o' clock"

            println("$hourEng $isOverHalf")
        }
    }
}

private fun changeEng(num: Int): String {
    return when (num) {
        1 -> "one"
        2 -> "two"
        3 -> "three"
        4 -> "four"
        5 -> "five"
        6 -> "six"
        7 -> "seven"
        8 -> "eight"
        9 -> "nine"
        10 -> "ten"
        11 -> "eleven"
        12 -> "twelve"
        13 -> "thirteen"
        14 -> "fourteen"
        15 -> "quarter"
        16 -> "sixteen"
        17 -> "seventeen"
        18 -> "eighteen"
        19 -> "nineteen"
        20 -> "twenty"
        21 -> "twenty one"
        22 -> "twenty two"
        23 -> "twenty three"
        24 -> "twenty four"
        25 -> "twenty five"
        26 -> "twenty six"
        27 -> "twenty seven"
        28 -> "twenty eight"
        29 -> "twenty nine"
        30 -> "half"
        else -> "error"
    }
}

 

풀이 과정

  • hour와 minutes를 입력받고 minutes가 1부터 30 사이인 경우와 31부터 59 사이인 경우와 0인 경우 3가지의 경우를 체크한다.
  • 1부터 30 사이인 경우는 past를 써야 한다. 또한 1인 경우는 minute로 출력해야 한다. 그 외는 minutes.
    minutes가 15 또는 30분인 경우는 "minutes"가 사용되지 않으므로 isOverHalf를 사용하지 않는다.
  • 31부터 59인 경우는 31~59를 영어로 변경하는 게 아닌 5시 55분이면 "6시 5분 전"과 같은 형식으로 출력되기 때문에 60에서 minutes를 빼서 몇 분이 남았는지 구한다.
    그냥 hour를 영어로 변환하는 게 아닌 다음 hour를 불러와야 한다.
    hour가 12면 13이 되는데 12시간 시계 형식이기 때문에 13이 되면 1로 돌아간다.
    60 - minutes가 15인 경우는 "minutes"가 사용되지 않으므로 isOverHalf를 사용하지 않는다. 30인 경우는 존재하지 않으므로 제외한다.
  • 0인 경우는 isOverHalf를 o' clock를 넣어준다.
  • 시, 분을 영어로 바꿔주는 함수인 changeEng()를 만들어 주었다.

 

문제 해결 과정

  • 고려해야 할 조건이 다양해서 실수가 나기 쉬운 문제이다. 다양한 조건을 고려해 생각해 볼 수 있었던 문제였던 것 같다.

 

2. 스파르타 코딩 클럽 강의 수강

A. Kotlin 문법 종합반 5주 차

1강(유용한 기능)

  1. 자료형 변환
    • 일반 자료형끼리는 to자료형() 메서드를 이용할 수 있다.
    • 문자열을 숫자로 변경할 때는 별도의 메서드가 필요.
      toInt(), toDouble(), toLong() 등등의 메서드가 존재함.
    • 객체 자료형 간의 변환은 상속관계에서만 가능하다.
      업 캐스팅: 자식클래스를 부모클래스의 자료형으로 객체를 생성한다. 자식의 정보는 부모로부터 받은 것이기 때문에 변환이 가능하다.
      다운 캐스팅: 부모클래스를 자식클래스의 자료형으로 객체를 생성한다. 부모의 방대한 정보를 자식이 모두 가질 수는 없기 때문에 변환이 불가능하다.
  1.  
  2. 자료형 타입 확인
    • is 키워드를 활용해 자료형의 타입을 확인할 수 있다.
      name is String => true or false
  3. 여러 인스턴스를 리턴
    • 메서드는 기본적으로 하나의 데이터를 리턴 하지만 2개, 3개의 데이터를 리턴하는 함수를 작성할 수 있다.
    • Pair<자료형, 자료형>을 이용해 2개의 타입을 리턴할 수 있고, Triple<자료형, 자료형, 자료형>을 이용해 3개의 타입을 리턴할 수 있다.

 

2강(확장 함수)

  • 확장 함수: 기존 클래스에 외부의 메서드를 추가할 수 있다.
    과도하게 사용하면 코드의 가독성을 해칠 수 있지만 장점도 존재함.
    원하는 메서드가 있지만 내가 설계한 클래스가 아닐 때 외부에서 메서드를 관리할 수 있다.
    내 목적을 위해 외부에서 관리하기 때문에 원본 클래스의 일관성을 유지한다.
// 확장 함수의 사용
fun Student.getGrade() = println("학생의 등급은 ${this.grade} 입니다")
var student = Student("참새", 10, "A+")
student.displayInfo()
student.getGrade()		// 확장 함수의 사용
  • 주의사항:
    public 멤버에게만 접근이 가능하다.
    클래스의 멤버함수처럼 상속이 불가하다.
    하위 클래스에서 함수를 재정의(오버라이딩)가 불가능하다.

 

4강(비동기 프로그래밍)

  • 생활 속에 숨어있는 비동기 작업
    5GB 영상 다운로드 -> 메일전송 -> 알림의 순서를 가진 로직이 있을 때, 해당 로직을 순차적으로 진행한다고 생각했을 때, 다른 작업을 하지 못하고 앱이 멈추는 문제가 발생할 수 있다.
    영상을 다운로드하면서 다른 작업을 하고 있다가 다운로드가 완료되면 알림을 보낼 수 있으면 좋겠다.
  • 비동기 프로그래밍: 여러 가지의 로직들이 완료 여부에 관계없이 실행되는 방식을 의미한다.
    순서대로 하나의 작업씩 수행하는 행위를 동기적 프로그래밍이라고 한다.
    동기적 프로그래밍은 순차적으로 수행하기 때문에 앞선 작업이 완료되자 않으면 뒷작업은 영원히 수행할 수 없다.
  • 결론
    동기 프로그래밍: 요청을 보내고 결과값을 받을 때까지 기다린다.
    비동기 프로그래밍: 요청을 보내고 결과값을 받을 때까지 멈추지 않고 또 다른 일을 수행한다.

 

5강(쓰레드)

  • 쓰레드: 기능을 동시에 실행할 수 있도록 도와준다.
    프로그램은 하나의 메인 쓰레드(실행 흐름)가 존재한다.
    하나의 메인 쓰레드는 fun main() 메인함수를 의미한다.
    여태 실습은 메인 쓰레드위에서 로직을 실행해서 동시처리가 불가했다.
    별도의 자식 쓰레드를 생성해 동시에 로직을 실행한다.
  • 프로세스: 프로그램이 메모리에 올라가서 실행될 때 이를 프로세스 1개라고 한다.
    프로세스 안에서 더 작은 작업의 단위를 쓰레드라고 한다.
    쓰레드는 생성돼서 수행할 때 각 독립된 메모리 영역인 stack을 가진다.
    즉, 쓰레드를 한 개 생성하면 스택 메모리의 일정 영역을 차지한다.

쓰레드가 생성된 메모리

// main 쓰레드 안에 하위 쓰레드 2개를 생성.
fun main() {
    thread(start = true) {		// 쓰레드 생성
        for(i in 1..10) {
            println("Thread1: 현재 숫자는 ${i}")
            runBlocking {		// 코루틴 코드의 일종
                launch {
                    delay(1000)
                }
            }
        }
    }

    thread(start = true) {
        for(i in 50..60) {
            println("Thread2: 현재 숫자는 ${i}")
            runBlocking {
                launch {
                    delay(1000)
                }
            }
        }
    }
}
  • 두 쓰레드가 1초 쉬고 나서(delay) 더 빠르게 cpu 자원을 할당받은 쓰레드의 코드가 먼저 실행된다.

 

6강(코루틴)

  • 코루틴: 운영체제의 깊이 있는 지식이 없어도 쉽게 비동기 프로그래밍을 할 수 있다
    최적화된 비동기 함수를 지원한다.
    안정적인 동시성, 비동기 프로그래밍을 가능하게 한다.
    쓰레드보다 더욱 가볍게 사용할 수 있다.
    로직들을 협동해서 실행하자는 것이 목표이고 구글이 적극 권장한다.
  • 코루틴은 빌더와 함께 사용한다.
    launch, async빌더를 주로 사용한다.
    launch: 결과값이 없는 코루틴.
    async: 결과값이 있는 코루틴, Deffered 타입으로 값을 리턴
  • 코루틴은  스코프로 범위를 지정할 수 있다.
    • GlobalScope: 앱이 실행된 이후에 계속 수행되어야 할 때 사용
    • CoroutineScope: 필요할 때만 생성하고, 사용 후에 정리가 필요하다.
  • 코루틴을 실행할 쓰레드를 Dispacher로 지정할 수 있다.
    • Dispachers.Main: UI와 상호작용하기 위한 메인 쓰레드
    • Dispachers.IO: 네트워크나 디스크 I/O작업에 최적화되어 있는 쓰레드.
    • Dispachers.Default: 기본적으로 CPU 최적화되어 있는 쓰레드.
  • 안드로이드에서는 특히 Dispacher 간의 변환을 해야 하는 작업을 고려해야 한다.

 

7강(쓰레드와 코루틴)

  • 동시성 프로그래밍
    • 쓰레드와 코루틴은 둘 다 동시성 프로그래밍을 지원하기 위한 기술이다.
    • 동시성 프로그래밍 기술의 context 스위칭이 중요하다.
  • 쓰레드와 코루틴의 차이점
    • 쓰레드
      작업 하나하나의 단위가 Thread임.
      각 쓰레드가 독립적인 stack 메모리를 가진다.
      운영체제 커널에 의한 Context Switching을 통해 동시성을 보장한다.
    • 코루틴
      작업 하나하나의 단위가 Coroutine Object임.
      여러 작업 각각에 Object를 할당.
      Coroutine Object로 객체이기 때문에 JVM Heap에 적재된다.
      소스 코드를 입력해 Switching 시점을 마음대로 정할 수 있다.
      Object 1이 Object 2의 결과를 기다릴 때 Object 1의 상태를 suspend로 바뀐다.
  • 요약
    • 쓰레드나 코루틴은 각자의 방법으로 동시성을 보장하는 기술.
    • 코루틴은 Thread를 대체하는 기술이 아니다. 하나의 Thread를 더욱 잘게 쪼개서 사용하는 기술이다.
    • 코루틴은 쓰레드보다 CPU를 절약하기 때문에 Light-Weight-Thread라고 한다.
    • 구글에서는 코루틴의 사용을 적극 권장하고 있다.

 

B. 기초 문법 강의(6월 10일 강연)

프로그래밍 언어

  • 프로그래밍 언어는 기계가 알아들을 수 있도록 해주는 언어
    사람과 기계가 커뮤니케이션할 수 있도록 중간다리 역할을 한다.

  • 컴파일러: 프로그래밍 언어를 기계어로 변환해 준다.
    코딩하다가 빨간 줄이 나오는 이유가 컴파일러가 돌면서 문법 오류를 찾아주기 때문이다.
  • 프로그래밍 언어는 저급 언어와 고급 언어가 있다. 고급 언어는 저급 언어에 비해 배우기 쉽다.
  • 잘 만들어진 코드: 가독성이 좋은(알아보기 편한) 코드, 성능이 좋은 코드, 유지 보수가 편한 코드.

 

함수

  • 함수: 어떠한 상자에 정보를 넣으면 원하는 결과가 나타나는 상자와 같은 개념.

  • 함수는 주로 중복되는 코드를 함수 하나로 관리할 수 있다.

 

객제지향과 클래스

  • 객체지향 언어의 배경: 클래스를 따로 만들어서 원하는 상황에 적절히 사용하기 위해 탄생한 개념.
  • 클래스: 객체 지향 프로그래밍에서 중요한 개념, 현실 세계의 객체를 모델링하기 위한 틀 또는 청사진이다.
    데이터(속성)와 그 데이터를 조작하는 데 필요한 함수를 하나의 단위로 묶은 것.
  • 클래스의 개본 구조
    • 속성(Properties): 클래스에 포함된 데이터의 요소이다.
    • 메서드(Methods): 클래스가 수행할 수 있는 동작 또는 기능.
    • 생성자(Constructor): 클래스의 인스턴스(객체)를 생성할 때 초기화하는 메서드.
      주 생성자와 부 생성자로 나뉜다.
  • 혼동하기 쉬운 개념
    • 파라미터(매개변수와 같은 말): 함수나 메서드 정의에서 사용되는 변수.
    • 전달 인자(Argument): 함수나 메서드를 호출할 때 전달되는 값.

 

클래스와 함수의 차이점

  • 구조화 및 캡슐화:
    • 클래스는 데이터와 데이터를 조작하는 메서드를 함께 묶어 캡슐화함. 이는 데이터와 기능을 구조화하는 데 도움을 줌.
    • 함수는 단독으로 존재하며 특정 작업을 수행하는 데 집중함.
  • 재사용성:
    • 클래스를 사용하면 코드의 재사용성이 높아진다. 클래스를 정의하면 동일한 특성과 동작을 가진 여러 객체를 쉽게 생성할 수 있다.
    • 함수는 특정 작업을 수행하는 코드를 재사용할 수 있게 해 주지만, 데이터 구조와 관련된 재사용성은 제공하지 않음.
  • 유지 보수:
    • 클래스를 사용하면 유지 보수가 용이해짐. 데이터와 메서드가 함께 묶여 있어 코드의 관련 부분을 쉽게 찾고 수정 가능.
    • 함수는 코드의 특정 부분만 수정할 때 용이함.
  • 추상화:
    • 클래스는 현실 세계의 개체를 모델링하여 추상화하는 데 사용.
    • 함수는 추상화 수준이 더 낮다. 주로 특정 작업을 수행하는 데 초점이 맞춰져 있다.

 

클래스가 필요한 이유

  • 클래스는 객체 지향 프로그래밍에서 데이터와 기능을 효율적으로 관리하고 조직화하는 데 사용함. 클래스를 사용하면 코드 재사용성, 유지보수성, 읽기 쉬움, 그리고 추상화 수준을 향상할 수 있다.
  • 함수는 간단하고 명확한 작업을 수행할 때 유용하지만, 전체 시스템을 모델링하고 관리하는 데는 클래스의 구조화된 접근 방식이 더 적합하다. 클래스와 함수를 함께 사용함으로써 더 강력하고 효율적인 프로그램을 만들 수 있다.

 

클래스 상속

  • 상속: 한 클래스가 다른 클래스의 속성과 메서드를 물려받는 것.
    코드의 재사용성을 높이고, 중복을 줄이며, 계층적 구조를 만들어 코드를 더욱 체계적으로 관리 가능함.
  • open 키워드: 상속을 하기 위해 무조건 클래스에 써야 하는 키워드
  • override 키워드: 상속받은 클래스에서 부모 클래스의 메서드나 속성을 자신에 필요에 맞게 변경하고자 할 때 사용.
  • 오버로드: 한 가지의 메서드의 파라미터의 타입을 다양하게 사용하는 것. 별도의 키워드는 없음.
  • 상속을 쓰는 이유: 코드 재사용성을 향상하고 유지 보수를 용이하게 하기 위해서이다.

 

추상화

  • 추상화: 복잡한 현실 세계의 개체나 시스템을 간단하고 이해하기 쉬운 모델로 변환하는 과정.
    수백 개의 클래스를 각각이 어떤 역할을 하는지 알 수 없기 때문에 추상 클래스/함수로 정의하고 꺼내 쓸 수 있다(override)
  • 상속과 추상화의 차이점
    • 상속은 이미 존재하는 클래스의 기능을 확장하거나 수정하는 것에 초점을 맞춘다. 코드의 재사용성과 유지보수성 제공.
    • 추상화는 복잡한 시스템을 간단하게 표현하는 것에 초점을 맞춘다. 시스템의 핵심적인 요소를 강조. 불필요한 세부 사항을 숨김.

 

상속과 추상화가 왜 필요하냐?

  • 코드를 간결하게
  • 유지보수를 편하게
  • 테스트를 효율적으로 하기 위해

 


 

오늘 공부 내용 정리 및 회고

  • 어제 특강 자료 정리와 코틀린 5주 차 강의를 들으면서 문법 기초를 더 탄탄히 다질 수 있었던 것 같다.
  • 현재 백준 골드 문제를 도전하고 있지만, 특정 알고리즘을 알고 있어야 풀 수 있는 문제가 골드 티어 이상 문제에는 많은 것 같다. 골드 티어 이상의 문제를 해결하기 위해 내일부터는 개인 과제를 하면서 특정 알고리즘을 공부하고, 공부한 알고리즘을 이용해 풀 수 있는 문제를 해결해 볼 생각이다.
728x90

댓글