반응형
오늘 공부한 내용 정리(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강(유용한 기능)
- 자료형 변환
- 일반 자료형끼리는 to자료형() 메서드를 이용할 수 있다.
- 문자열을 숫자로 변경할 때는 별도의 메서드가 필요.
toInt(), toDouble(), toLong() 등등의 메서드가 존재함. - 객체 자료형 간의 변환은 상속관계에서만 가능하다.
업 캐스팅: 자식클래스를 부모클래스의 자료형으로 객체를 생성한다. 자식의 정보는 부모로부터 받은 것이기 때문에 변환이 가능하다.
다운 캐스팅: 부모클래스를 자식클래스의 자료형으로 객체를 생성한다. 부모의 방대한 정보를 자식이 모두 가질 수는 없기 때문에 변환이 불가능하다.
- 자료형 타입 확인
- is 키워드를 활용해 자료형의 타입을 확인할 수 있다.
name is String => true or false
- is 키워드를 활용해 자료형의 타입을 확인할 수 있다.
- 여러 인스턴스를 리턴
- 메서드는 기본적으로 하나의 데이터를 리턴 하지만 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
반응형
'♞ | 공부일지 > ♝ | TIL' 카테고리의 다른 글
[Android, 내일배움캠프] 공부일지(2024-06-13) (0) | 2024.06.13 |
---|---|
[Android, 내일배움캠프] 공부일지(2024-06-12) (0) | 2024.06.12 |
[Android, 내일배움캠프] 공부일지(2024-06-10) (0) | 2024.06.10 |
[Android, 내일배움캠프] 공부일지(2024-06-07) (0) | 2024.06.07 |
[Android] 공부일지(2024-06-06) (0) | 2024.06.07 |