News

관용적 Kotlin으로 Advent of Code 퍼즐 풀기

Read this post in other languages:

코드를 작성하는 것 외에 언어를 배우는 가장 좋은 방법은 무엇일까요? Advent of Code와 같은 재미있고 짧은 과제를 해결하는 것 또한 언어 능력을 향상시키는 데 도움이 될 수 있을 뿐만 아니라, 동일한 문제를 다른 사람이 해결한 방법과 자신의 방법을 비교해 보면서 많은 것을 배울 수 있습니다.

Kotlin 팀을 포함하여 전 세계의 많은 개발자들이 Eric Wastl이 만든 Advent of Code 챌린지에 참여합니다. Advent of Code는 매년 12월에 게시되는 일련의 과제로, 과제를 해결하고 다른 사람과 경쟁합니다. 그리고 지금은 크리스마스와 새해를 기념할 최고의 강림절 시기입니다!

커뮤니티에 관용적 Kotlin을 배우고자 하는 분위기를 확산하고 앞으로 더 많은 개발자가 Kotlin을 사용하여 Advent of Code 과제를 풀어 볼 수 있도록, JetBrains는 Advent of Code 2020 과제의 솔루션을 준비하기로 결정했습니다. 이 글이 이미 이전에 이 문제를 풀었거나, 지금 이 문제를 풀 준비가 되었거나, 단지 풀이 과정을 확인해보고 싶은 모든 분들께 유용하기를 바랍니다. 물론, 이 글을 읽기 전에 해당 과제를 스스로 해결해 보는 것이 가장 좋습니다!

첫 번째 과제에 대한 풀이 과정과 동영상은 아래에서 확인하세요. 동영상에는 한국어 자막이 포함되어 있습니다. 이런 형태의 자료가 유익하다고 생각하고, 이 외에 여러 과제에 대한 풀이 과정을 원하시면 아래 댓글에 의견을 남겨 주세요.

1일 차. Report Repair(보고서 수정)

경비 보고서를 수정하고 있습니다! https://adventofcode.com/2020/day/1에서 전체 과제 설명을 확인하세요.*

숫자 목록에서 합이 2020이 되는 2개의 숫자(두 번째 부분에서는 3개)를 찾은 그 2개 숫자(또는 3개)를 곱합니다.

과제 해결 방법

https://adventofcode.com/에서 등록하고 https://adventofcode.com/2020/day/1에서 과제를 열어 Kotlin으로 해결 방법을 작성한 다음, 사이트에서 결과를 확인하세요. Kotlin 코드는 온라인에서 작성하거나 IDE에서 작성할 수 있습니다.

마지막으로 자신의 해답을 아래의 해답과 비교합니다.

코드를 바로 넣을 수 있게 src 폴더를 소스 세트로 표시했습니다. 편의를 위해 src/day1/input.txt와 같은 입력 파일을 소스 폴더에 복사했습니다. 이 프로젝트에서 해답을 확인할 수 있습니다.

해설

다음은 예제입니다.

먼저, 입력을 읽고 이를 분석해야 합니다. Kotlin readLines() 함수를 사용하여 주어진 파일에서 줄 목록을 읽을 수 있습니다.

readLines()는 문자열 목록을 반환하고 이를 숫자 목록으로 변환합니다.

이 코드를 프로그램의 진입점인 main 함수 안에 넣습니다. 입력을 시작하면 IntelliJ IDEA가 java.io.File을 자동으로 가져옵니다.

이제 목록을 반복하고 각 숫자에 대해 반복 실행을 수행하여 합계를 확인할 수 있습니다.

이 코드를 main 안에 넣으면 필요한 숫자가 발견되었을 때 main에서 return을 반환합니다.

마찬가지로 3개 숫자의 합도 확인합니다.

이 코드를 실행하고 주어진 입력 값에 대한 결과를 얻을 수 있습니다. 이상 입니다! 첫 번째 과제는 정말 간단합니다.

그러나 이 방법은 각 요소에 대해 동일한 목록을 반복해서 실행합니다. 두 개의 숫자를 찾기 위한 두 개의 중첩 루프가 있으면 N2회의 연산이 이루어집니다. 여기서 N은 요소의 수입니다. 3개의 숫자를 찾아야 하는 경우, 3개의 중첩 루프와 N3회의 연산이 필요합니다. 숫자 목록이 큰 경우에는 이 방법은 이러한 유형의 문제를 해결하는 가장 효율적인 방법이 아닙니다. 분명 더 좋은 방법이 있지 않을까요?

네, 당연히 더 효과적인 방법이 있고 Kotlin 표준 라이브러리가 이를 간결하게 표현하는 데 도움을 줄 수 있습니다. 긴 계산을 결과를 찾는 데 사용되는 일종의 스마트 스토리지로 대체할 수 있고, 실제 많이 그렇게 하고 있습니다.

두 숫자에 대한 과제 해결하기

먼저 숫자 “보수”에 대한 맵을 작성해 보겠습니다. 보수는 주어진 숫자와 합하여 2020이 되는 숫자를 말합니다.

Kotlin associateBy 함수를 사용하여 맵을 작성합니다. 그 람다 인수는 목록 요소를 저장하는 이 맵의 키를 반환합니다. 샘플 입력의 경우 다음 맵이 됩니다.

이 절차가 끝나면 답을 명확하게 볼 수 있습니다! 목록의 첫 번째 숫자 1721보수 맵에 1721=299 키로 표시됩니다. 즉, 이것이 숫자 299에 대한 보수이고 합계는 2020입니다.

이 정보를 맵에 저장하면 이 맵에 목록의 숫자에 보수가 있는지 확인할 수 있습니다. 다음 코드는 기존 보수가 있는 첫 번째 숫자를 찾습니다.

각 숫자를 숫자와 그 보수(보수가 존재하는 경우)로 구성된 쌍으로 변환한 다음 첫 번째 null이 아닌 결과를 찾습니다.

mapNotNull을 사용하여 목록의 각 요소를 변환하고 모든 null 결과를 필터링합니다. 이는 첫 번째 map을 호출한 다음 결과에 대해 filterNotNull을 호출하는 것을 단축한 것입니다.

firstOrNull은 목록의 첫 번째 요소를 반환하거나 목록이 비어 있으면 null을 반환합니다. Kotlin 표준 라이브러리는 종종 OrNull 접미사를 사용하여 실패 시 예외를 발생시키지 않고 null을 반환하는 함수를 표시합니다(예: elementAtOrNull, singleOrNull< /0> 또는 maxOrNull).

Kotlin 1.5.0부터 두 개의 결과적 연산인 mapNotNullfirst(OrNull)을 하나의 함수 호출 firstNotNullOf(OrNull)로 바꿀 수도 있습니다.

보조적인 구조를 구축한 후, 이전처럼 N2이 아닌 N 연산의 결과로 합이 2020이 되는 2개의 숫자를 찾을 수 있었습니다!

이 2개의 숫자를 곱해야 하므로 마지막 단계는 다음과 같습니다.

pair 변수는 두 숫자의 nullable Pair를 포함하거나, 초기 목록에서 합이 2020이 되는 숫자가 없으면 null입니다. 안전한 액세스 ?.let 함수와 함께 사용하고 pairnull이 아닌 경우 결과를 표시하기 위해 람다 구문의 구조 분해를 사용합니다.

세 개의 숫자에 대한 과제 해결하기

다음 단계는 세 개의 숫자에 대해 이 문제를 해결하는 것입니다. 지금까지 수행한 작업을 재사용하고 합하여 주어진 숫자가 되는 숫자 쌍을 찾는 논리를 별도의 함수로 추출해 보겠습니다.

firstNotNullOfOrNull 함수도 사용했습니다.

이제 findPairOfSum을 사용하여 각 숫자 및 이 숫자와 함께 합이 2020이 되는 보수의 값 쌍을 저장하는 보조 맵을 작성하겠습니다.

동일한 초기 입력에 대해 보수 쌍 맵은 다음과 같습니다.

이전과 마찬가지로 답을 확인할 수 있습니다! 맵에서 null이 아닌 쌍에 해당하는 숫자입니다.

그러나 실제로 전체 맵을 작성할 필요는 없습니다. null이 아닌 쌍에 해당하는 첫 번째 숫자만 찾으면 됩니다! 이미 많은 분들에게 친숙한 firstNotNullOfOrNull 함수를 사용하여 찾아보겠습니다.

Kotlin의 간결한 구문에 주목하세요. 함수는 표현식을 직접 반환할 수 있습니다.

마지막 단계는 이전에 했던 것과 같이 세 개의 숫자 결과 값이 null이 아닌 경우 이를 곱하는 것입니다.

이상입니다!

다음 글에서는 2일차 과제를 해결하는 방법에 대해 설명하겠습니다. 이 글이 유용하다고 생각되고 더 많은 과제에 대한 풀이를 원한다면 저희에게 알려주세요!

*Advent of Code(Eric Wastl)의 허락 하에 사용하였습니다.

게시물 원문 작성자

Jessie Cho

Svetlana Isakova

image description

Discover more