오늘 공부한 내용 정리(2024년 6월 5일)
1. 코드카타 문제풀이
A. CCW(백준, 11758번)
문제 내용
문제 풀이 방법
- 문제 이름에 힌트가 나와있다. ccw 알고리즘을 이용해 문제를 해결한다.
- ccw 알고리즘은 3개의 점의 방향을 알 수 있는 알고리즘이다.
해결 코드(스포 주의)
더보기
import java.io.BufferedReader
import java.io.InputStreamReader
fun main() = with(BufferedReader(InputStreamReader(System.`in`))) {
val pointList = mutableListOf<List<Int>>()
for (i in 0 until 3) {
val point = readLine().split(" ").map { it.toInt() }
pointList.add(point)
}
// ccw 알고리즘: 각 점의 방향성을 파악할 수 있다.
// 점들의 좌표를 정의한 후 사선 공식(신발끈 공식)을 이용해 문제를 해결할 수 있다.
// x: x1, y1 y: x2, y2 z: x3, y3
// x1 x2 x3 x1
// y1 y2 y3 y1
val ccw = ccw(
x1 = pointList[0][0], y1 = pointList[0][1],
x2 = pointList[1][0], y2 = pointList[1][1],
x3 = pointList[2][0], y3 = pointList[2][1]
)
if (ccw > 0) println(1)
else if (ccw < 0) println(-1)
else println(0)
}
private fun ccw(x1: Int, y1: Int, x2: Int, y2: Int, x3: Int, y3: Int): Int {
val a = x1 * y2 + x2 * y3 + x3 * y1
val b = y1 * x2 + y2 * x3 + y3 * x1
return a - b
}
풀이 과정
- 좌표를 입력받고 ccw 알고리즘을 이용한다.
- 점 3개의 좌표가 주어졌을 때(x: x1, y1, y: x2, y2, z: x3, y3) 사선 공식(신발끈 공식)을 이용해 문제를 해결한다.
- 빨간 빗금의 값을 a, 파란 빗금의 값을 b라고 할 때, a - b를 해서 점 3개의 방향성(ccw)을 숫자로 알 수 있다.
- ccw가 0보다 크면 시계 방향이므로 -1을, 작으면 반시계 방향으로 1을, 0인 경우는 직선이므로 0을 출력한다.
문제 해결 과정
- ccw알고리즘을 알고 있으면 쉽게 문제를 해결할 수 있다.
- ccw의 사선 공식을 알고 있으면 점들 간의 방향을 알 수 있다.
- ccw는 기하학 알고리즘 중 가장 자주 쓰이는 알고리즘이라고 한다. 제대로 이해해서 유용하게 써먹자.
- https://degurii.tistory.com/47#code
2. 스파르타 코딩 클럽 강의 수강
A. Kotlin 문법 종합반 4주 차
1강(접근 제한자)
- 접근 제한자: 변수나 메서드의 접근을 제한한다.
public, private, internal, protected로 접근을 제한한다. - public: 명시하지 않으면 기본적으로 public(어디서나 접근 가능)
- private: 동일한 클래스 내부에서만 접근 가능
- internal: 같은 모듈 내부에서만 접근 가능
- protected: 기본적으로 private이지만 상속을 받은 경우에 타 모듈에서 접근 가능
- 접근 제한자를 사용하는 이유
전근권한을 설정해 무분별한 접근을 차단할 수 있다.
클래스들 간의 접근하면 안 되는 상황을 구분하기 때문에 유지 보수에 용이하다.
2강(예외 처리)
- 예외 처리: 프로그램 실행 도중 발생하는 예외를 적절히 처리하는 것.
예외는 프로그램 실행 중에 발생하는 컴파일 에러인데, 예외가 발생하면 프로그램이 비정상적으로 종료된다.
// 기본적인 try-catch문 사용 방법
try {
예외가 발생할 가능성이 존재하는 코드
} catch(예외종류) {
예외가 발생했을때 처리할 코드
} finally {
try-catch와 함께 수행되는 코드
}
// throw 키워드를 사용해 예외를 강제로 발생시킬 수 있다.
if(num1 > 10) {
throw 예외종류
}
- 예외 처리를 하는 이유
고품질의 프로그램은 사용성을 해치지 않아야 한다.
프로그램이 비정상 종료되는 것은 심각한 문제이다.
미리 예외가 발생하는 경우를 생각하고 소스 코드를 작성해야 안정성이 높을 프로그램을 만들 수 있다.
3강(지연 초기화)
- 지연 초기화: 변수나 상수의 값을 나중에 초기화할 수 있다.
코틀린은 클래스를 설계할 때, 안정성을 위해 반드시 변수의 값을 초기화할 것을 권장한다.
클래스를 정의할 때, 초기의 값을 정의하기 난처한 상황이 올 때 나중에 대입하기 위한 방법이다. - lateinit, lazy 키워드를 사용해 지연 초기화를 할 수 있다.
변수: lateinit, 상수: lazy - 지연 초기화를 적절히 사용해 컴퓨터의 메모리를 더 효율적으로 사용할 수 있다.
// lateinit 기본적인 사용 방법.
lateinit var name:String
var age:Int = 0
// isInitialized로 lateinit 변수가 초기화되어 있는지 체크할 수 있다.
if(this::name.isInitialized) {
println("이름은: ${name} 입니다.")
println("나이는: ${age} 입니다.")
} else {
println("name변수를 초기화해주세요.")
}
// lazy 기본적인 사용 방법
val address: String by lazy {
println("address 초기화")
"seoul"
}
// lazy 변수가 호출되는 시점에 초기화 코드를 수행한다.
println("주소는: ${address} 입니다.")
4강(널 세이프티)
- null safety: 코틀린의 널 안정성을 향상시킬 수 있다.
null은 프로그램의 기용성을 저하시키는 치명적인 오류이다.
코틀린은 null로부터 안전한 설계를 위해 자료형에 null 여부를 설정할 수 있다. - ?, !!, ?:, ?. 키워드를 지원한다.
- 강제로 null이 아니라고 단정짓는 !!은 최대한 사용을 지양해야 한다.
// nullable한 자료형 타입의 변수 선언.
var address:String? = null
address = "서울"
println("주소는: ${address} 입니다")
// !! 키워드는 null이 아니라고 단정지을 때 사용한다.
var inputData = readLine()!!
var data = inputData.toInt()
println("Null아닌 값: ${data}")
// ?. 키워드는 null인지 확인하고 null이 아닐 때만 데이터 참조.
lateinit var name:String
var address:String? = null
println("주소의 길이는: ${address?.length} 입니다") // address가null인 경우는 null을 return.
// 엘비스 연산자(?:)를 이용해 데이터가 null인 경우의 로직을 따로 처리할 수 있다.
println("주소의 길이는: ${address?.length ?: "초기화하세요"} 입니다")
6강(배열)
- 배열: 변수에 순서를 매겨 사용할 수 있다.
일반적으로 변수를 선언하면, 코틀린은 메모리에 띄엄띄엄 변수를 랜덤으로 생성한다.
변수의 위치정보가 연속적이지 않기 때문에 순서가 없다. - 배열을 이용해 데이터에 순서를 매겨 연속적으로 활용할 수 있다.
배열을 사용하기 위해 arrayOf() 함수를 사용한다.
// 기본적인 배열 사용법
var arr = arrayOf(1,2,3,4,5)
// 배열요소를 모두 출력합니다
println(Arrays.toString(arr))
// 배열의 0번째 데이터 출력
println(arr[0])
- 배열을 사용해서 반복적으로 변수를 생성 및 접근하는 행위를 방지할 수 있다.
7강(컬렉션)
- 컬렉션(collection): 개발에 유용한 자료 구조들을 제공한다.
코틀린에서는 대표적으로 List, Set, Map을 제공한다.
// list의 기본 사용법.
// 읽기전용 리스트 생성
var scores1 = listOf(값1, 값2, 값3)
// 수정가능 리스트입니다
// 0번, 1번, 2번 인덱스에 접근해서 값을 변경할 수 있다.
var scores2 = mutableListOf(값1, 값2, 값3)
scores2.set(인덱스, 값)
// 수정가능 리스트입니다
// array로 데이터들을 저장하는 ArrayList도 mutableListOf와 동일하게 사용 가능.
var scores3 = ArrayList<자료형>(값1, 값2, 값3)
scores3.set(인덱스, 값)
// map 기본 사용법
// map은 key, value로 데이터가 구성된다.
// 읽기전용 map 생성.
// 변수명[키]로 데이터에 접근.
var scoreInfo1 = mapOf("kor" to 94, "math" to 90, "eng" to 92)
println(scoreInfo1["kor"])
// 수정가능 map 생성.
var scoreInfo2 = mutableMapOf("kor" to 94, "math" to 90)
scoreInfo2["eng"] = 92
println(scoreInfo2["eng"])
// set 기본 사용법
// set은 중복 없이 데이터를 관리할 수 있다.
// 읽기전용 set 생성.
var birdSet = setOf("닭", "참새", "비둘기")
// 수정가능 Set입니다.
var mutableBirdSet = mutableSetOf("닭", "참새", "비둘기")
mutableBirdSet.add("꿩")
mutableBirdSet.remove("꿩")
// set은 합집합, 차집합, 교집합 함수를 지원해 간편하게 필요한 요소를 추출할 수 있다.
var birdSet = setOf("닭", "참새", "비둘기", "물오리")
var flyBirdSet = setOf("참새", "비둘기", "까치")
// 합집합 사용
var unionBirdSet = birdSet.union(flyBirdSet)
// 교집합 사용
var intersectBirdSet = birdSet.intersect(flyBirdSet)
// 차집합 사용
var subtractBirdSet = birdSet.subtract(flyBirdSet)
8강(람다)
- 람다(lambda): 메서드를 간결하게 정의할 때 사용한다.
자바 8과 동일하게 람다식을 지원한다.
하나의 메서드를 간결하게 표현할 수 있다.
// 람다의 기본 사용법
함수 이름() {매개변수1, 매개변수2... ->
// 코드
}
// 중괄호를 생략하는 방식.
fun add(num1:Int, num2:Int, num3:Int) = (num1+num2+num3)/3
// 변수에 괄호를 전개해 데이터를 집어넣는 방식
var add = {num1: Int, num2: Int, num3: Int -> (num1+num2+num3) / 3}
3. 개인 공부
A. 개인 과제(콘솔 계산기)
계산 함수 구성하기.
추상 클래스를 이용해 더하기(+), 빼기(-), 곱하기(*), 나누기(/) 연산을 할 수 있도록 추상 함수를 만들어 주었다.
abstract class AbstractOperation {
// 덧셈
abstract fun add(num1: Int, num2: Int): Int
// 뺄셈
abstract fun minus(num1: Int, num2: Int): Int
// 곱셈
abstract fun multiply(num1: Int, num2: Int): Int
// 나눗셈
abstract fun divide(num1: Int, num2: Int): Int
}
Calculator 클래스를 생성해 생성한 추상 클래스를 상속받아 추상 메서드들을 오버라이딩해 기능을 정의해 주었다.
class Calculator: AbstractOperation() {
override fun add(num1: Int, num2: Int): Int {
return num1 + num2
}
override fun minus(num1: Int, num2: Int): Int {
return num1 - num2
}
override fun multiply(num1: Int, num2: Int): Int {
return num1 * num2
}
override fun divide(num1: Int, num2: Int): Int {
return num1 / num2
}
}
마지막으로 콘솔에 계산기를 println()과 무한 반복문을 이용해 구현한다.
import src.calculate.Calculator
import java.util.Scanner
fun main() = with(Scanner(System.`in`)) {
println("=========================계산기 v1=========================")
println("계산을 시작할까요?")
println("계산을 시작하려면 0을 입력하고, 종료하려면 -1을 입력하세요.")
var isCalculatorFinish = false
while (true) {
// 계산기 종료 여부가 true면 종료
if (isCalculatorFinish) break
val mainInput = next()
// mainInput을 올바르게 작성시키기 위한 try catch
if (mainInput == "0") {
// 계산기 시작.
println("계산을 시작합니다.")
while (true) {
println("사칙연산식을 입력하세요(종료하려면 ` 입력).")
val formula = next()
if (formula == "`") {
println("계산기를 종료합니다.")
println("=========================계산기 v1=========================")
isCalculatorFinish = true
break
} else {
// 정규 표현식을 이용한 수식 분할(여러 개의 문자를 기준으로 분할 가능함).
val splitFormula = formula.split("+", "-", "*", "/")
val splitFormulaSymbol = formula.filter { it in setOf('+', '-', '*', '/') }.map { it.toString() }
// splitFormula에서 숫자와 괄호"()"만 포함되도록 하기(정규 표현식 사용).
if (!splitFormula.all { it.matches(Regex("\\d*")) })
println("연산식이 문자를 포함하고 있습니다. 다시 시도해 주세요.")
else {
calculation(
splitFormula = splitFormula,
splitFormulaSymbol = splitFormulaSymbol
)
}
}
}
} else if (mainInput == "-1") {
// 계산기 종료
println("계산기를 종료합니다.")
println("=========================계산기 v1=========================")
break
} else {
println("올바른 숫자를 입력하세요.")
println("계산을 시작하려면 0을 입력하고, 종료하려면 -1을 입력하세요.")
continue
}
}
}
- 계산을 시작할 때와 계산을 종료할 때 같은 상황들이 있을 때, 입력을 받아서 계산을 시작하거나 종료하도록 했다.
- 먼저 String으로 입력받고(int로 정의하고 문자열을 입력받았을 때의 예외 상황을 방지) 입력받은 String이 "0"이면 계산을 시작하고 "-1"이면 종료하도록 했다.
다른 문자 또는 숫자가 입력되면 else로 이동해 예외 print문을 출력하고 다시 입력받을 수 있도록 만들어 주었다. - mainInput이 "0"일 때, 계산식을 입력받을 수 있게 해 주었다.
연산식을 입력받기 시작했을 때에도 언제든지 계산기를 종료할 수 있도록 "`"기호를 입력했을 때, 계산용 반복(내부 반복)을 빠져나가고 true로 변경한 isCalculatorFinish를 이용해 외부 반복도 종료해 프로그램을 완전히 끝낼 수 있도록 만들었다. - "`"기호가 아닌 경우에는 계산식을 입력받는데, 수식에 이상한 문자들(+, -, *, / 를 제외한 모든 문자)이 입력되면 안 되기 때문에 +, -, /, * 기호를 기준으로 수식을 list로 나누고(split 함수, splitFormula 변수) 정규 표현식을 이용해 splitFormula의 데이터가 숫자 이외의 문자를 포함하고 있으면 예외 print를 출력한다.
- 아닌 경우에는 계산을 진행할 수 있도록 만드는 중이다.
현재 사칙연산식이 수식 부호로 끝나는 경우의 처리와 사칙연산 우선순위(*, /가 먼저)를 구현하지 못했다.
오늘 공부 내용 정리 및 회고
- 코틀린 4주 차 강의를 들었다. 원래는 3주 차까지 듣는 것이 이번주의 진도인데 미리 예습할 겸 4주 차 강의까지 들었다.
- 콘솔을 이용한 서비스를 만드는 것은 오랜만에 하는 거라 설레기도(?)하고 어색하기도 했다.
어떻게 해서 잘 구현은 하고 있는 것 같다.
728x90
'♞ | 공부일지 > ♝ | TIL' 카테고리의 다른 글
[Android, 내일배움캠프] 공부일지(2024-06-07) (0) | 2024.06.07 |
---|---|
[Android] 공부일지(2024-06-06) (0) | 2024.06.07 |
[Android, 내일배움캠프] 공부일지(2024-06-04) (0) | 2024.06.04 |
[Android, 내일배움캠프] 공부일지(2024-06-03) (0) | 2024.06.03 |
[Android, 내일배움캠프] 공부일지(2024-05-31) (0) | 2024.05.31 |