<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>dev-junku.log</title>
        <link>https://velog.io/</link>
        <description>'개발'은 '예술'이고 '서비스'는 '작품'이다</description>
        <lastBuildDate>Mon, 29 May 2023 07:50:42 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>dev-junku.log</title>
            <url>https://images.velog.io/images/dev-junku/profile/3d964fdf-1d21-47e5-a761-611f6cfdb680/social.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. dev-junku.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/dev-junku" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Java VS Kotlin Lambda Function]]></title>
            <link>https://velog.io/@dev-junku/Java-VS-Kotlin-Lambda-Function</link>
            <guid>https://velog.io/@dev-junku/Java-VS-Kotlin-Lambda-Function</guid>
            <pubDate>Mon, 29 May 2023 07:50:42 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[.class를 왜 사용하는 걸까?]]></title>
            <link>https://velog.io/@dev-junku/.class%EB%A5%BC-%EC%99%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EA%B1%B8%EA%B9%8C</link>
            <guid>https://velog.io/@dev-junku/.class%EB%A5%BC-%EC%99%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EA%B1%B8%EA%B9%8C</guid>
            <pubDate>Sun, 21 May 2023 13:57:27 GMT</pubDate>
            <description><![CDATA[<p>본 글은 <code>김영한님의 스프링 핵심 원리 - 기본편</code>을 본 이후에 작성되었습니다.</p>
<p>오늘 강의를 들으면서 .class를 왜 사용하는걸까? 라는 의문이 들었다.</p>
<p>사실 안드로이드 코드를 짤 때 intent 이후 특정 Activity를 호출하기 위해 <code>::class.java</code>라는 코드를 썼었는 데, 그때는 왜 쓰는지 몰랐다.</p>
<p>이번에는 그냥 넘어가기 좀 그래서 알아보았다.</p>
<p>내가 궁금했던 부분은 @Configuration과 @Bean을 사용하여 Spring Container에 구현체를 Bean으로 등록한 이후 AnnotationConfigApplicationContext에 AppConfig.class를 넘겨줄 때 왜..? 라는 생각이 들었다.</p>
<p>코드는 다음과 같다.</p>
<pre><code class="language-java">ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);</code></pre>
<h2 id="1-annotationconfigapplicationcontext에서-받는-인자는-무엇인가">1. AnnotationConfigApplicationContext()에서 받는 인자는 무엇인가?</h2>
<p><code>AnnotationConfigApplicationContext</code> 코드를 보면 다음과 같이 되어 있다.</p>
<pre><code class="language-java">public AnnotationConfigApplicationContext(Class&lt;?&gt;... componentClasses) {
        this();
        register(componentClasses);
        refresh();
}</code></pre>
<p><code>Class</code>라는 Arguement를 받고 있고, 이 <code>componentClasses</code>를 register()라는 method로 등록하는 것을 확인할 수 있다.</p>
<p>그러면 .class 객체를 넘기는 데, instance를 넘기는 건지? 아니면 다른 특정값을 넘기는건지 생각이 든다.</p>
<p>아! 근데 확실한 것은 instance를 넘기진 않는다. 왜냐하면 <code>new</code> 키워드가 없기 때문이다. 그럼 AppConfig.class가 곧 Class Type이라는 건데, Class는 무엇인가?</p>
<h2 id="2-class는-무엇인가">2. <code>Class</code>는 무엇인가?</h2>
<p><code>Class</code>는 다음과 같이 나타나 있다.</p>
<pre><code class="language-java">public final class Class&lt;T&gt; implements java.io.Serializable,
                              GenericDeclaration,
                              Type,
                              AnnotatedElement,
                              TypeDescriptor.OfField&lt;Class&lt;?&gt;&gt;,
                              Constable {
    private static final int ANNOTATION= 0x00002000;
    private static final int ENUM      = 0x00004000;
    private static final int SYNTHETIC = 0x00001000;

    private static native void registerNatives();
    static {
        registerNatives();
    }

    private Class(ClassLoader loader, Class&lt;?&gt; arrayComponentType) {
        // Initialize final field for classLoader.  The initialization value of non-null
        // prevents future JIT optimizations from assuming this final field is null.
        classLoader = loader;
        componentType = arrayComponentType;
    }
</code></pre>
<blockquote>
<ul>
<li>코드를 보면 public 생성자가 없다. 다 private으로 생성된다. 그러면, 해당 객체의 생성은 당연히 JVM이 알아서 통제한다. </li>
</ul>
</blockquote>
<blockquote>
<ul>
<li>따라서 거의 모든 Java의 자료형(참조, 원시 포함)은 Class 객체로 표현할 수 있다.</li>
</ul>
</blockquote>
<p>그러면 .class를 쓰면 어떻게 바뀌는가?</p>
<h2 id="3--classclassjava를-쓰면-어떻게-바뀌는가">3  <code>.class(::class.java)</code>를 쓰면 어떻게 바뀌는가?</h2>
<blockquote>
<p><code>.class</code>는 <code>클래스 리터럴</code>을 나타내는 특별한 표기법이다. 이 클래스 리터럴은 class의 메타정보를 갖고 있는 데, class 리터럴을 통해 해당 클래스의 메타 정보를 갖고 올 수 있다.</p>
</blockquote>
<p>무엇인지는 좀 더 봐야하겠지만, 아래의 코드를 보면 어느 정도 감이 온다.</p>
<pre><code class="language-java">    @CallerSensitive
    public static Class&lt;?&gt; forName(String name, boolean initialize,
                                   ClassLoader loader)
        throws ClassNotFoundException
    {
        Class&lt;?&gt; caller = null;
        @SuppressWarnings(&quot;removal&quot;)
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            // Reflective call to get caller class is only needed if a security manager
            // is present.  Avoid the overhead of making this call otherwise.
            caller = Reflection.getCallerClass();
            if (loader == null) {
                ClassLoader ccl = ClassLoader.getClassLoader(caller);
                if (ccl != null) {
                    sm.checkPermission(
                        SecurityConstants.GET_CLASSLOADER_PERMISSION);
                }
            }
        }
        return forName0(name, initialize, loader, caller);
    }</code></pre>
<p><code>forName</code>이라는 method는 Class&lt;?&gt; 타입을 return하고 있다. 그러면 무엇을 리턴하는가?</p>
<p>어떤 클래스의 이름, 초기화, loader, caller를 담아 return하고 있다. 해당 정보가 바로 메타 정보로 사용되는듯 싶다.</p>
<p>java의 경우 <code>.class</code>를 사용하나, kotlin의 경우 <code>::class.java</code>를 사용한다. </p>
<h2 id="4-class를-사용하면-사용된-class의-instance가-생성되는가">4. .class를 사용하면, 사용된 class의 instance가 생성되는가?</h2>
<blockquote>
<p>아니다. 생성되지 않는다. forName을 통해 해당 클래스의 메타정보만 갖고올 뿐 인스턴스가 생성되는 것은 아니다.</p>
</blockquote>
<hr>
<p>그러면 앞서 작성했던</p>
<pre><code class="language-java">ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);</code></pre>
<p>는 kotlin에서 아래와 같이 사용하면 된다.</p>
<pre><code class="language-kotlin">val applicationContext = AnnotationConfigApplicationContext(AppConfig::class.java)</code></pre>
<hr>
<p>결국 <code>.class(::class.java)</code>를 사용하는 이유는 해당 class의 메타 정보를 얻기 위해서 사용된다. <code>.getClass()</code>를 사용해도 같은 결과를 얻을 수 있으니, 참고하자.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Java / Kotlin Class의 의존관계 주입(의존성 주입)]]></title>
            <link>https://velog.io/@dev-junku/Java-Kotlin-Class%EC%9D%98-%EC%9D%98%EC%A1%B4%EA%B4%80%EA%B3%84-%EC%A3%BC%EC%9E%85%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%A3%BC%EC%9E%85</link>
            <guid>https://velog.io/@dev-junku/Java-Kotlin-Class%EC%9D%98-%EC%9D%98%EC%A1%B4%EA%B4%80%EA%B3%84-%EC%A3%BC%EC%9E%85%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%A3%BC%EC%9E%85</guid>
            <pubDate>Sun, 07 May 2023 14:20:43 GMT</pubDate>
            <description><![CDATA[<p>본 글은 김영한님의 <code>스프링 핵심 원리 - 기본편</code>을 본 이후에 작성되었습니다.</p>
<p>프로젝트의 할인 정책이 만약에 <code>정액 할인</code>에서 <code>정률 할인</code>으로 바뀐다면 어떻게 될까?</p>
<blockquote>
<p>기존의 할인 정책인 고정 할인(FixDiscountPolicy)이 정률(RateDiscountPolicy)로 바뀌어야 한다.</p>
</blockquote>
<h3 id="java">Java</h3>
<p>해당 클래스는 이미 구현이 완료된 상태이며, 주문시 작동해야 한다.</p>
<p>그렇다면, 어떻게 해야할까?</p>
<pre><code class="language-java">import hello.core.discount.DiscountPolicy;
import hello.core.member.data.Member;
import hello.core.member.repository.MemberRepository;
import hello.core.order.data.Order;
import hello.core.order.service.OrderService;

public class OrderServiceImpl implements OrderService {

    private final MemberRepository memberRepository = new MemoryMemberRepository();
    private final DiscountPolicy discountPolicy = new FixDiscountPolicy;

    @Override
    public Order createOrder(Long memberId, String itemName, int itemPrice) {

        Member member = memberRepository.findById(memberId);
        int discountPrice = discountPolicy.discount(member, itemPrice);

        return new Order(memberId, itemName, itemPrice, discountPrice);
    }
}</code></pre>
<p>기존의 <code>OrderServiceImpl</code> class를 보면, <code>MemoryMemberRepository</code>와 <code>FixDiscountPolicy</code>에 의존하고 있다.</p>
<p>이는 <code>DIP(의존 역전 원칙)</code>를 위반하고 있다. 따라서 RateDiscountPolicy인 정률 할인 클래스가 구현이 완료된다고 해도, OrderServiceImpl에 Field 주입을 진행해야 한다. 즉, <code>OrderServiceImpl를 수정해야 하는 문제</code>가 생겨버린다.</p>
<p>결국에는 처음부터 Achitecture를 잘못 설계해버려서 Order에 관여될 때 마다 관련된 모든 code를 수정해야 하는 번거로움이 생겨버린다.</p>
<p>그렇기 때문에, 지금 바로 class(구현체)가 아닌, interface 또는 abstract class에 의존할 수 있도록 DIP를 준수하도록 code를 수정해보자.</p>
<p>우선 Field로 구현체를 주입하고 있는 Code를 다음과 같이 interface에 의존하도록 바꾸어보자.</p>
<pre><code class="language-java">import hello.core.discount.DiscountPolicy;
import hello.core.member.data.Member;
import hello.core.member.repository.MemberRepository;
import hello.core.order.data.Order;
import hello.core.order.service.OrderService;

public class OrderServiceImpl implements OrderService {

    private MemberRepository memberRepository;
    private DiscountPolicy discountPolicy;

    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }

    @Override
    public Order createOrder(Long memberId, String itemName, int itemPrice) {

        Member member = memberRepository.findById(memberId);
        int discountPrice = discountPolicy.discount(member, itemPrice);

        return new Order(memberId, itemName, itemPrice, discountPrice);
    }
}</code></pre>
<p>이제 <code>OrderServiceImpl</code> class는 <code>MemberRepository</code>, <code>DiscountPolicy</code> interface에 의존하게 되었다.</p>
<p>위 코드는 interface 타입이 들어와 생성자를 생성하고 있으며, 해당 클래스를 주입해주면 된다.</p>
<blockquote>
<p>어떻게 주입할 수 있을까?</p>
</blockquote>
<p>답: 구현제 주입을 담당하는 class를 따로 만든다.</p>
<p>다음과 같이 AppConfig class를 만들어 구현제만을 관리하면 된다.</p>
<pre><code class="language-java">import hello.core.discount.DiscountPolicy;
import hello.core.discount.implement.FixDiscountPolicy;
import hello.core.member.repository.MemberRepository;
import hello.core.member.repository.MemberRepositoryImpl;
import hello.core.member.service.MemberService;
import hello.core.member.service.MemberServiceImpl;
import hello.core.order.implement.OrderServiceImpl;
import hello.core.order.service.OrderService;

public class AppConfig {


   /*...*/

    public OrderService orderService() {
        return new OrderServiceImpl(
                memberRepository(),
                disiscountPolicy()
        );
    }

    private MemberRepository memberRepository() {
        return new MemberRepositoryImpl();
    }

    private DiscountPolicy disiscountPolicy() {
        return new FixDiscountPolicy();
    }
}</code></pre>
<p>위 코드를 살펴보면, <code>OrderService</code> 타입인 <code>OrderServiceImpl</code>를 return할 때, <code>memberRepository()</code>, <code>disiscountPolicy()</code>를 주입하고 있으며, 해당 method는 각각의 구현체를 return 하고 있다.</p>
<p>이제 원래의 목적인 할인 정책의 변동이 찾아왔을 때, FixDiscountPolicy()를 RateDiscountPolicy()로 바꾼다면, 곧바로 적용되는 것을 확인할 수 있다.</p>
<p>이렇게 Field가 아닌 생성자를 통해 주입하는 방식을 <code>생성자 의존관계 주입</code>이라고 한다.</p>
<h3 id="kotlin">Kotlin</h3>
<p>아래는 Kotlin으로 바꾸었을 때의 코드이다.</p>
<p>kotlin으로 생성자 주입을 진행했을 때는 다음과 같이 <code>OrderServiceImpl</code>를 만들어주면 된다.</p>
<pre><code class="language-kotlin">import hello.corekotlin.discount.DiscountPolicy
import hello.corekotlin.member.repository.MemberRepository
import hello.corekotlin.order.data.Order
import hello.corekotlin.order.service.OrderService

class OrderServiceImpl(
    private val memberRepository: MemberRepository,
    private val discountPolicy: DiscountPolicy
): OrderService {

    override fun createOrder(memberId: Long, itemName: String, itemPrice: Int): Order {
        val member = memberRepository.findById(memberId)
        val discountPrice = discountPolicy.discount(member, itemPrice)

        return Order(
            memberId,
            itemName,
            itemPrice,
            discountPrice
        )
    }
}</code></pre>
<p>위 코드를 java로 Decompile했을 때, 생성자가 자동으로 만들어진다.</p>
<p>또한 AppConfig는 다음과 같다.</p>
<pre><code class="language-kotlin">import hello.corekotlin.discount.DiscountPolicy
import hello.corekotlin.discount.implement.RateDiscountPolicy
import hello.corekotlin.member.repository.MemberRepository
import hello.corekotlin.member.repository.MemoryMemberImpl
import hello.corekotlin.member.service.MemberService
import hello.corekotlin.member.service.MemberServiceImpl
import hello.corekotlin.order.implement.OrderServiceImpl
import hello.corekotlin.order.service.OrderService

class AppConfig {

    /*...*/

    fun orderService(): OrderService = OrderServiceImpl(
        memberRepositoy(),
        discountPolicy()
    )

    fun memberRepositoy(): MemberRepository = MemoryMemberImpl()

    fun discountPolicy(): DiscountPolicy = RateDiscountPolicy()

}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Boot Java & Kotlin Test Code (코틀린스럽게)]]></title>
            <link>https://velog.io/@dev-junku/Spring-Boot-Java-Kotlin-Test-Code-%EC%BD%94%ED%8B%80%EB%A6%B0%EC%8A%A4%EB%9F%BD%EA%B2%8C</link>
            <guid>https://velog.io/@dev-junku/Spring-Boot-Java-Kotlin-Test-Code-%EC%BD%94%ED%8B%80%EB%A6%B0%EC%8A%A4%EB%9F%BD%EA%B2%8C</guid>
            <pubDate>Mon, 01 May 2023 08:32:43 GMT</pubDate>
            <description><![CDATA[<p>본 글은 김영한님의 <code>스프링 핵심 원리 - 기본편</code>을 본 이후에 작성되었습니다.</p>
<p>오늘은 공부를 하는 도중에 기록했으면 좋겠다고 생각한 것이 있어 글을 작성하게 되었습니다.</p>
<p>현재 김영한님의 <code>스프링 핵심 원리 - 기본편</code>에서 주문과 할인 도메인 실행과 테스트를 듣고 있습니다. 여기서 할인 청책 테스트 코드를 작성하는 부분에서 자바코드를 kotlin으로 바꾸면서 진행하고 있는데요.</p>
<p>여기서는 코틀린의 scope function을 통해 간단히 자바 Test Code를 코틀린 Test Code로 바꾸어볼게요.</p>
<p>이번 Test는 단순히 정률 할인 정책 Class가 잘 작동하고 있는지 알아보는 Test입니다.</p>
<h3 id="java-code">Java Code</h3>
<pre><code class="language-java">import hello.core.member.data.Grade;
import hello.core.member.data.Member;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

class RateDiscountPoilcyTest {

    RateDiscountPoilcy rateDiscountPoilcy = new RateDiscountPoilcy();

    @Test
    @DisplayName(&quot;VIP는 10% 할인이 적용되어야 한다.&quot;)
    void vip_o() {
        //given
        Member member = new Member(
                1L,
                &quot;memberA&quot;,
                Grade.VIP
        );
        // when
        int discount = rateDiscountPoilcy.discount(member, 10000);

        // then
        Assertions.assertThat(discount).isEqualTo(1000);
    }


    @Test
    @DisplayName(&quot;VIP가 아니면, 할인이 적용되면 안 된다.&quot;)
    void vip_x() {
        // Given
        Member member = new Member(
                1L,
                &quot;memberA&quot;,
                Grade.BASIC
        );
        // When
        int discount = rateDiscountPoilcy.discount(member, 10000);

        // Then
        Assertions.assertThat(discount).isEqualTo(0);
    }
}</code></pre>
<p>위 코드의 흐름은 정말 단순하고 이해하기 쉽죠.</p>
<ol>
<li><p>RateDiscountPoilcy를 주입해줍니다. (따라서 RateDiscountPoilcyTest Class는 RateDiscountPoilcy Class에 의존하고 있습니다.)</p>
</li>
<li><p>Member 객체를 정의합니다.</p>
</li>
<li><p>rateDiscountPoilcy에 member와 가격(price)를 던져줍니다.</p>
</li>
<li><p>반환 받은 dicount를 Assertions에 던져 실제 금액과 예상 금액이 맞는지 확인합니다.</p>
</li>
</ol>
<p>그래서 VIP와 BASIC 2가지의 카테고리에 대한 테스트 함수 모두 같은 로직으로 흘러가고 있습니다.</p>
<p>그리고 아래는 제가 작성한 Kotlin Test Code 입니다.</p>
<h3 id="kotlin-code">Kotlin Code</h3>
<pre><code class="language-kotlin">import hello.corekotlin.member.data.Grade
import hello.corekotlin.member.data.Member
import org.assertj.core.api.Assertions
import org.junit.jupiter.api.Test

internal class RateDiscountPolicyTest {

    val rateDiscountPolicy = RateDiscountPolicy()

    @Test
    fun vip_o() {
        Member(
            1L,
            &quot;memberA&quot;,
            Grade.VIP
        ).apply {
            Assertions
                .assertThat(rateDiscountPolicy.discount(this, 10000))
                .isEqualTo(1000)
        }
    }

    @Test
    fun vip_x() {
        Member(
            1L,
            &quot;memberA&quot;,
            Grade.BASIC
        ).apply {
            Assertions
                .assertThat(rateDiscountPolicy.discount(this, 10000))
                .isEqualTo(0)
        }
    }
}</code></pre>
<p>member 객체를 만들고 이를 apply로 바로 넘겨서 테스트를 진행!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Boot Java & Kotlin h2Database 연결]]></title>
            <link>https://velog.io/@dev-junku/Spring-Boot-Java-Kotlin-h2Database-%EC%97%B0%EA%B2%B0</link>
            <guid>https://velog.io/@dev-junku/Spring-Boot-Java-Kotlin-h2Database-%EC%97%B0%EA%B2%B0</guid>
            <pubDate>Sun, 05 Mar 2023 05:37:27 GMT</pubDate>
            <description><![CDATA[<p>해당 내용은 배달의 민족 김영한님의 <code>스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술</code> 강의를 듣고 작성되었습니다.</p>
<p>이번 강의에서는 h2Database를 import 하는 과정에 대해 기록</p>
<p>Java건 Kotlin이건 Spring에서 h2Database를 연결할 때</p>
<p>다음과 같은 과정만 거치면 된다.</p>
<ol>
<li><p>h2Database 설치</p>
</li>
<li><p>h2Database 터미널 실행(절대 끄지 말 것)</p>
</li>
<li><p>Gradle import &amp; Synchronize</p>
<ul>
<li><p>Java</p>
<pre><code class="language-java">
implementation &#39;org.springframework.boot:spring-boot-starter-jdbc&#39;
runtimeOnly &#39;com.h2database:h2&#39;</code></pre>
</li>
<li><p>Kotlin</p>
<pre><code class="language-kotlin">
implementation(&quot;org.springframework.boot:spring-boot-starter-jdbc&quot;)
runtimeOnly(&quot;com.h2database:h2&quot;)</code></pre>
</li>
</ul>
</li>
<li><p>application.properties</p>
<ul>
<li><p>Java &amp; Kotlin</p>
<pre><code>
spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.driver-class-name=org.h2.Driver</code></pre></li>
</ul>
</li>
<li><p>Run 확인</p>
<ul>
<li><p>Java
<img src="https://velog.velcdn.com/images/dev-junku/post/af03392b-f0aa-48ad-a58d-5b5a101f4ac6/image.png" alt=""></p>
</li>
<li><p>Kotlin
<img src="https://velog.velcdn.com/images/dev-junku/post/7d9b4d5f-646f-44e7-8d64-fc9071371f11/image.png" alt=""></p>
</li>
</ul>
</li>
</ol>
<p><code>- 끝 -</code></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Boot Java & Kotlin Test Code 종속성 주입의 차이점]]></title>
            <link>https://velog.io/@dev-junku/Spring-Boot-Java-Kotlin-Test-Code-%EC%A2%85%EC%86%8D%EC%84%B1-%EC%A3%BC%EC%9E%85%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90</link>
            <guid>https://velog.io/@dev-junku/Spring-Boot-Java-Kotlin-Test-Code-%EC%A2%85%EC%86%8D%EC%84%B1-%EC%A3%BC%EC%9E%85%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90</guid>
            <pubDate>Sun, 12 Feb 2023 10:10:44 GMT</pubDate>
            <description><![CDATA[<p>Java Test Code를 Kotlin Test Code로 변환하는 도중 기록할 만한 것이 생겨 기록!</p>
<p>해당 내용은 배달의 민족 김영한님의 <code>스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술</code> 강의를 듣고 작성되었습니다.</p>
<p>일반적으로 Java를 통해 TestCode DI를 진행할 때, Service와 같은 repo를 사용하게 만들기 위해 생성자를 통해 주입한다. 하지만, Kotlin의 경우 @Autowired를 사용하면, 굳이 DI를 진행하지 않아도 자동으로 만들어 준다.</p>
<p>혹시 몰라 Autowired로 연결된 2개의 클래스를 java로 Decompile 했는데, 아니나 다를까 @Autowired를 통해 생성자를 주입받고 있었다.</p>
<p>정말 가능한 기능이었다.</p>
<p>아래는 @Autowired를 통해 자동으로 생성자를 주입받은 kotlin code이다.</p>
<pre><code class="language-kotlin">@SpringBootTest
internal class MemberServiceTest @Autowired constructor(
    private val memoryMemberRepository: MemoryMemberRepository,
    private val memberService: MemberService,
) {
    ...
}</code></pre>
<p>그리고 이를 java code로 compile하면 다음과 같은 코드로 요약된다.</p>
<pre><code class="language-java">
public class MemberServiceTest {
   private final MemoryMemberRepository memoryMemberRepository;
   private final MemberService memberService;

   ...

   @Autowired
   public MemberServiceTest(@NotNull MemoryMemberRepository memoryMemberRepository, @NotNull MemberService memberService) {
      Intrinsics.checkNotNullParameter(memoryMemberRepository, &quot;memoryMemberRepository&quot;);
      Intrinsics.checkNotNullParameter(memberService, &quot;memberService&quot;);
      super();
      this.memoryMemberRepository = memoryMemberRepository;
      this.memberService = memberService;
   }
}</code></pre>
<p>kotlin에서 <code>private val</code>로 생성했기 때문에</p>
<p>java에서도 <code>private final</code>로 작성되었고</p>
<p>이를 외부에서 주입하고 있는 모습을 확인할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BOJ] 전쟁 - 전투 & 케빈 베이컨의 6단계 법칙 in Python]]></title>
            <link>https://velog.io/@dev-junku/BOJ-%EC%A0%84%EC%9F%81-%EC%A0%84%ED%88%AC-%EC%BC%80%EB%B9%88-%EB%B2%A0%EC%9D%B4%EC%BB%A8%EC%9D%98-6%EB%8B%A8%EA%B3%84-%EB%B2%95%EC%B9%99-in-Python</link>
            <guid>https://velog.io/@dev-junku/BOJ-%EC%A0%84%EC%9F%81-%EC%A0%84%ED%88%AC-%EC%BC%80%EB%B9%88-%EB%B2%A0%EC%9D%B4%EC%BB%A8%EC%9D%98-6%EB%8B%A8%EA%B3%84-%EB%B2%95%EC%B9%99-in-Python</guid>
            <pubDate>Tue, 24 Jan 2023 09:47:49 GMT</pubDate>
            <description><![CDATA[<p>요 며칠 연휴여서 심심하기도 하고 오랜만에 알고리즘을 풀었습니다. 작정하고 어려운 문제를 건든게 아니어서.. 좀 쑥스럽네용..</p>
<p><a href="https://www.acmicpc.net/problem/1389">케빈 베이컨의 6단계 법칙, 문제풀러가기!</a></p>
<p>전형적인 데이크스트라 문제로 간단하게 풀었습니다.</p>
<p>graph 만들고 실제로 순회할 부분만 따로 보기 위해 peoples라는 set을 만들었습니다. 문제에서 범위만 주어졌을 뿐이지 실제로 존재하는 노드가 1부터 차례로 시작인지는 확실하게 읽지를 않아서 확실한 node만 담기 위한 목적으로 서용했습니다.</p>
<p>다음은 똑같습니다. 데이크스트라 알고리즘을 사용하되, peoples에 있는 원소만 순회하여 문제에서 말한 level(제곱수의 합)을 구합니다. 그 중 가장 낮은 것을 찾으면 끝입니다.</p>
<pre><code class="language-python">import sys
from heapq import heappop, heappush
input = sys.stdin.readline

N, M = map(int, input().split())

graph = [[] for _ in range(N+1)]
INF = int(1e9)
peoples = set()

for _ in range(M):
    one, two = map(int, input().split())
    graph[one].append((1, two))
    graph[two].append((1, one))
    peoples.add(one)
    peoples.add(two)

peoples = list(peoples)
peoples.sort()

friends = [[0 for _ in range(N)] for _ in range(N)]

def dijkstra(start):

    init_dist = [INF for _ in range(N+1)]
    init_dist[start] = 0
    HQ = []
    heappush(HQ, (0, start))

    while HQ:
        cost, now = heappop(HQ)

        if init_dist[now] &lt; cost: continue

        for add_cost, nxt in graph[now]:
            new_cost = cost + add_cost

            if init_dist[nxt] &gt; new_cost:
                init_dist[nxt] = new_cost
                heappush(HQ, (init_dist[nxt], nxt))

    return init_dist

number = INF
answer = -1

for i in range(1, N+1):
    summm = sum(dijkstra(i)[1:])
    if number &gt; summm:
        answer = i
        number = summm

print(answer)   </code></pre>
<p><a href="https://www.acmicpc.net/problem/1303">전쟁 - 전투, 문제풀러가기!</a></p>
<p>그냥 전형적인 dfs 문제</p>
<p>상 하 좌 우로 돌면서 dfs를 진행하되 전역으로 중요 정보를 관리, hash를 사용하여 답안 도출!</p>
<p>간단하게 풀었습니다.</p>
<pre><code class="language-python">import sys
input = sys.stdin.readline

N, M = map(int, input().split())

graph = [list(input().strip()) for _ in range(M)]
visited = [[False for _ in range(N)] for _ in range(M)]

dx = [0, -1, 0, 1]
dy = [1, 0, -1, 0]

dic = {
    &quot;W&quot;: 0,
    &quot;B&quot;: 0
}

def dfs(y, x):
    global num

    visited[y][x] = True

    for idx in range(4):
        nx, ny = x + dx[idx], y + dy[idx]

        if not (0 &lt;= nx &lt; N and 0 &lt;= ny &lt; M): continue
        if visited[ny][nx]: continue
        if graph[ny][nx] != target: continue
        num += 1
        dfs(ny, nx)


for i in range(M):
    for j in range(N):
        if visited[i][j]: continue
        target = graph[i][j]
        num = 1
        dfs(i, j)
        dic[target] += num**2

for value in dic.values():
    print(value, end = &quot; &quot;)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 - 1 Kotlin Code Refactoring (김영한님 강의)]]></title>
            <link>https://velog.io/@dev-junku/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9E%85%EB%AC%B8-%EC%BD%94%EB%93%9C%EB%A1%9C-%EB%B0%B0%EC%9A%B0%EB%8A%94-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-%EC%9B%B9-MVC-DB-%EC%A0%91%EA%B7%BC-%EA%B8%B0%EC%88%A0-1-Kotlin-Code-Refactoring-%EA%B9%80%EC%98%81%ED%95%9C%EB%8B%98-%EA%B0%95%EC%9D%98</link>
            <guid>https://velog.io/@dev-junku/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9E%85%EB%AC%B8-%EC%BD%94%EB%93%9C%EB%A1%9C-%EB%B0%B0%EC%9A%B0%EB%8A%94-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-%EC%9B%B9-MVC-DB-%EC%A0%91%EA%B7%BC-%EA%B8%B0%EC%88%A0-1-Kotlin-Code-Refactoring-%EA%B9%80%EC%98%81%ED%95%9C%EB%8B%98-%EA%B0%95%EC%9D%98</guid>
            <pubDate>Tue, 24 Jan 2023 09:12:04 GMT</pubDate>
            <description><![CDATA[<p>Spring 공부에 진전이 없기도 하고.. 유튭 영상보다가 우아콘에서 kotlin 실력을 늘릴려면, Java 코드를 Kotlin으로 Refactoring 하면 직방이라고 해서 아주아주 기초부터 다시 시작하자는 마음으로 김영한 강사님의 스프링 입문 강의부터 시작해서 Java코드를 Kotlin으로 바꿔보자는 다짐을 했다. 얼마나 갈지는 모르지만, 그래도 꾸준히 해보자...</p>
<p>그럼 바로 바꾸다가 기록할 만한 것부터 시작!</p>
<h3 id="1-optional은-kotlin에서-굳이-쓰지-않아도-된다">1. Optional은 Kotlin에서 굳이 쓰지 않아도 된다.</h3>
<p>코드를 바꾸면서 Java SpringBoot의 경우 Optional로 Null값을 컨트롤하는 것을 보았다. 근데 굳이 Kotlin에서는 Optional 객체를 사용하지 않아도 된다. 무엇보다 Java에서 Kotlin으로 refactoring하는 이유가 코드 가독성도 어느 정도 포함되어 있으면서, Kotlin 문법을 최대한 활용해야 하는데, 코드가 거의 같다면 Refactoring 할 이유가 없으니 말이다.</p>
<p>따라서 강의에 나온 Repository</p>
<pre><code class="language-java">import hello.hellospring.domain.Member;
import java.util.List;
import java.util.Optional;

public interface MemberRepository {

    Member save(Member member);

    Optional&lt;Member&gt; findById(Long id);

    Optional&lt;Member&gt; findByName(String name);

    List&lt;Member&gt; findAll();

}</code></pre>
<p>는 Kotlin으로 다음과 같이 변경할 수 있다.</p>
<pre><code class="language-kotlin">package kotlinhellospring.demo.repository

import kotlinhellospring.demo.domain.Member

interface MemberRepository {

    fun save(member: Member): Member

    fun findById(id: Long): Member?

    fun findByName(name: String): Member?

    fun findAll(): List&lt;Member&gt;
}</code></pre>
<p><code>?</code>를 통해 Null값을 체크할 수 있으니, 굳이 Optional을 사용하지 않아도 된다.</p>
<p>또한 Kotlin은 코드를 java로 바꾸면 생각보다 많은 내용이 함축되어 있다. 이는 kotlin → java로 변경하면 바로 보인다.</p>
<pre><code class="language-java">public interface MemberRepository {
   @NotNull
   Member save(@NotNull Member var1);

   @Nullable
   Member findById(long var1);

   @Nullable
   Member findByName(@NotNull String var1);

   @NotNull
   List findAll();
}</code></pre>
<p>위 코드는 kotlin코드를 java로 Decompile 한 것이다. kotlin에서의 <code>?</code> 여부에 따라 Java <code>@Notnull</code> <code>@Nullable</code>이 생성되는 것을 확인 할 수 있다.</p>
<p>그리고 MemberRepository 구현체 중 <code>Optional</code>이 사용된 부분을 보자.</p>
<p>java를 보면 다음과 같다.</p>
<pre><code class="language-java">    @Override
    public Optional&lt;Member&gt; findByName(String name) {

        return store.values()
                .stream()
                .filter(
                        member -&gt; member.getName().equals(name)
                )
                .findFirst();
    }</code></pre>
<p>위와 같은 코드를 Kotlin에서는 아래와 같이 표현할 수 있다.</p>
<pre><code class="language-kotlin">    override fun findByName(name: String): Member? = store
        .values
        .find {
            member -&gt; member.name == name
        }</code></pre>
<p>kotlin의 find는 찾는 즉시 해당 첫번 째 객체를 반환한다.</p>
<p>그리고 또한 java처럼 Stream에 굳이 올릴 필요가 없다. kotlin 내부에서 모두 처리 가능하기 때문에.. 왜냐면 java에서 Stream으로 올리는 이유가 lambda를 사용하여 코드 가독성과 함수형 코드를 진행하기 위해서인데, kotlin은 모두 지원해주기 때문이다.</p>
<h3 id="2-kotlin-test-코드-작성-시-주의-repository-component-annotation으로-bean-등록-및-springboottest-autowired-constructor-annotation-사용">2. Kotlin Test 코드 작성 시 주의! Repository, Component Annotation으로 Bean 등록 및 SpringBootTest, Autowired Constructor Annotation 사용.</h3>
<p>난.. 말하는 감자도 아닌, 말도 못하는 감자다.. 해당 문제로 인해 몇 시간 동안 답을 찾으러 Google과 씨름했다..ㅠ</p>
<p>그거슨 바로 아래의 오류...!!</p>
<blockquote>
<p><code>No ParameterResolver registered for parameter in constructor.......</code></p>
</blockquote>
<p>Kotlin에서는 왜인지는 모르겠는데, Bean 등록이 잘 안..ㅎㅎ</p>
<p>그래서 구현한 부분을 Bean으로 등록하려면 @Component를 사용하던, @Repository를 사용하던 @Service를 사용하던 Bean으로 등록해야 한다.</p>
<p>또한 Test할 클래스 위에 @SpringBootTest를 붙여준다. 거기에 사용할 Bean으로 등록된 구현체를 생성자로 활용하기 위해서는</p>
<p><code>@Autowired constructor</code>를 지정해준다. 우선 java 코드는 다음과 같다.</p>
<h4 id="java-code">Java Code</h4>
<p><code>MemoryMemberRepository class</code> 파일</p>
<pre><code class="language-java">import hello.hellospring.domain.Member;
import java.util.*;

public class MemoryMemberRepository implements MemberRepository {
    private static Map&lt;Long, Member&gt; store = new HashMap&lt;&gt;();
    private static long sequence = 0L;
    ...
}</code></pre>
<p>java는 @SpringBootTest를 붙이지 않아도 된다. AutoWired할 필요도 없다. 또한 아래 처럼</p>
<p><code>MemoryMemberRepositoryTest class</code> 파일</p>
<pre><code class="language-java">import hello.hellospring.domain.Member;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

class MemoryMemberRepositoryTest {

    MemoryMemberRepository memoryMemberRepository = new MemoryMemberRepository();

    @Test
    public void save() {
        ...
        // 여기서 테스트를 그냥 진행해도 아무런 문제가 없다.
    }

}</code></pre>
<p>굳이 Bean으로 등록하지 않아도 알아서 관리해준다.</p>
<p>이제 Kotlin을 보자.</p>
<h4 id="kotlin-code">Kotlin Code</h4>
<p><code>MemoryMemberRepository class</code> 파일</p>
<pre><code class="language-kotlin">import kotlinhellospring.demo.domain.Member
import org.springframework.stereotype.Repository
import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.HashMap

/**
 * bean으로 등록하여 관리하려면
 * Component 또는 Repository Annotation을 사용하여 등록하면 된다.
 * 테스트 코드가.. 안ㄷ...
 */
@Repository
class MemoryMemberRepository : MemberRepository {

    companion object {
        /**
         * 가변성 제어는 다음 시간에
         */
        var store: HashMap&lt;Long, Member&gt; = hashMapOf()
        var sequence: Long = 0L
    }
}</code></pre>
<p>위 구현체를 Bean으로 등록하기 위해 @Repository 또는 @Componenet를 사용한다.</p>
<p><code>MemoryMemberRepositoryTest class</code> 파일</p>
<pre><code class="language-kotlin">import kotlinhellospring.demo.domain.Member
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest

@SpringBootTest
class MemoryMemberRepositoryTest @Autowired constructor(
    private val memoryMemberRepository: MemoryMemberRepository
) {

    @Test
    fun save() {
        ...
    }

}</code></pre>
<p>또한 Test룰 하겠다는 명시적 의미와 생성자 Parameter를 사용하여 위해 @SpringBootTest와 @Autowired constructor를 지정한다.</p>
<hr>
<p>프로젝트에서 굳이 Kotlin으로 바꿔할 이유가 없다면,, 안바꿔도 되지 않을까? 그래도 Kotlin으로 작성했을 때 코드 생산성이 증가하는건 인정!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[🖇 java → kotlin 기초 (class)]]></title>
            <link>https://velog.io/@dev-junku/java-kotlin-%EA%B8%B0%EC%B4%88-class</link>
            <guid>https://velog.io/@dev-junku/java-kotlin-%EA%B8%B0%EC%B4%88-class</guid>
            <pubDate>Sun, 23 Oct 2022 09:26:49 GMT</pubDate>
            <description><![CDATA[<p>최근 Spring에서 java → kotlin으로 Refactoring 하는 것에 무척이나 관심이 많아졌다. 그래서 오늘은 java가 kotlin으로 어떻게 변경될 수 있을지 알아보는 것으로 한 주를 시작해보려고 한다. 아주 기초적으로...</p>
<blockquote>
<p>본 코드는 Fastcampus의 <code>실무 프로젝트로 배우는 Kotlin &amp; Spring : 리팩토링부터 서비스 구현까지</code> 라는 강의 보고 작성되었습니다.</p>
</blockquote>
<p>지금부터 할 것은 자바의 main method를 작동시키면서 getter, setter는 koltin으로 되어 있는 경우를 사용하는 코드이다.</p>
<p>kotlin으로 getter, setter를 구현하는 경우는 매우 간편하다. data class를 통해 hashCode나 equals, toString를 자동으로 구현하게 할 수 있지만, 단순히 <code>getter, setter를 구현하는 것</code>이라면, kotlin의 <code>class</code>를 사용해도 상관없다. 다음과 같이 말이다.</p>
<h2 id="💉-kotlin의-getter-setter-그리고-java">💉 Kotlin의 getter, setter 그리고 Java</h2>
<p>본래 kotlin의 코드는 다음과 같다.</p>
<pre><code class="language-kotlin">import java.util.*

class Person {

    var name: String? = null

    var birthDate: LocalDate? = null
    // LocalDate는 따로 import 해주어야 한다.

    val age: Int = 10

}</code></pre>
<p>위 코드를 java로 변경하면, 다음과 같이 바뀌게 된다.</p>
<pre><code class="language-java">import java.util.*

public final class Student {
   @Nullable
   private String name;
   @Nullable
   private LocalDate birthDate;
   private final int age = 10;

   @Nullable
   public final String getName() {
      return this.name;
   }

   public final void setName(@Nullable String var1) {
      this.name = var1;
   }

   @Nullable
   public final LocalDate getBirthDate() {
      return this.birthDate;
   }

   public final void setBirthDate(@Nullable LocalDate var1) {
      this.birthDate = var1;
   }

   public final int getAge() {
      return this.age;
   }
}</code></pre>
<p>code를 작성해야 할 것이 확실히 줄어드는 것이다. 코드 생산성이 압도적으로 높아진다.</p>
<p>물론 <code>kotlin에서 java로</code> 어떻게 변경되는지 개인적으로 <code>무조건 알아야 된다</code>고 생각든다.</p>
<ol>
<li>kotlin의 class는 java에선 <code>final</code>이다.<ul>
<li>무차별 상속을 막기 위해서 만들어진 기능이다.</li>
<li>대규모 시스템의 경우 객체 지향의 특성 상 상속으로 인해 생기는 종속성을 최대한 방지하기 위한다고 생각하면, 왜 final을 default로 내렸는지 이해된다. 그리고 이러한 기능은 함수(method)에도 적용된다.</li>
<li>참고로 상속을 하고 싶다면, class앞에 <code>open</code>이라는 keyword를 작성하면 된다.</li>
</ul>
</li>
<li>kotlin의 var 키워드를 작성하면 getter와 setter가 자동으로 만들어진다.</li>
</ol>
<p>kotlin에서의 class는 getter와 setter는 자동으로 만들지만, hashcode와 equals, toString은 만들어지지 않는다. 이점을 유의해야 한다.</p>
<p>그리고 이 둘 모두 java의 main이건, kotlin의 main이건 상관없이 작동한다.</p>
<pre><code class="language-java">public class Main {
    public static void main(String [] args) {

        Student student = new Student();
        student.setName(&quot;junku&quot;);
        sutdent.setBirthDate(LocalDate.of(2022, 10, 23));

        System.out.println(student.getName()); // junku
        System.out.println(student.getBirthDate()); // 2022-10-23
    }
}</code></pre>
<p>그러면 지금의 반대는 어떻게 될까? java의 class를 이용하면서 main을 kotlin으로 두고 code를 짜면 다음과 같다.</p>
<pre><code class="language-java">import java.util.*

public class Student {

    private String name;

    private LocalDate birthDate;

    private int age;

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getBirthDate() {
        return this.birthDate.toString();
    }

    public void setBirthDate(LocalDate birthDate) {
        this.birthDate = birthDate;
    }

    public String getAge() {
        return this.age;
    }
}</code></pre>
<p>java의 class를 위와 같이 작성하고 kotlin의 경우 다음과 같이 작성하면 된다.</p>
<pre><code class="language-kotlin">
fun main() {
    val person = Person()
    person.setName(&quot;junku&quot;)
    person.setBirthDate(LocalDate.of(2022, 10, 22))

    println(person.getName()) // junku
    println(person.getBirthDate()) // 2022-10-22

    // 사실 이러한 접근 보다는 kotlin에서는 property로 접근하는 것을 권장함.

    println(person.name) // junku
    println(person.birthDate) // 2022-10-22
}</code></pre>
<p>그리고 무엇보다. 다음과 같은 method를 java class에 추가했다고 해보자.</p>
<pre><code class="language-java">import java.time.LocalDate;
import java.util.UUID;

public class Person {

    ...

    public String getUUID() {
        return UUID.randomUUID().toString();
    }
}</code></pre>
<p>코드에 있는 그대로 UUID라는 property는 없다. 하지만, kotlin은 이 메소드의 name을 통해 property에 접근할 수 있다.</p>
<p>왜냐하면 앞의 get이라는 keyword를 보고 알아서 만들어 두기 때문이다, 하지만 method의 name 앞에 <code>get</code>이 없다면, 만들지 않아서 property로 접근이 불가능하다는 점을 기억하자.</p>
<p>즉 현재는 아래의 코드가 가능하다.</p>
<pre><code class="language-kotlin">
fun main() {

    val student = Student()
    println(student.uuid) // 9e778488-b671-4c40-a0f8-8b05f751b2e4

}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[🗻 코틀린 Scope 함수]]></title>
            <link>https://velog.io/@dev-junku/%EC%BD%94%ED%8B%80%EB%A6%B0-Scope-%ED%95%A8%EC%88%98</link>
            <guid>https://velog.io/@dev-junku/%EC%BD%94%ED%8B%80%EB%A6%B0-Scope-%ED%95%A8%EC%88%98</guid>
            <pubDate>Mon, 17 Oct 2022 14:08:53 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/dev-junku/post/ad81c005-78ba-4c53-9900-ced464f57ec3/image.png" alt=""></p>
<h1 id="kotlin식-문법-scope-함수">Kotlin식 문법: Scope 함수</h1>
<p>코틀린의 scope 함수는 객체 조작을 훨씬 더 쉽게 하기 위해서 Kotlin에서 지원하는 함수의 일종임</p>
<p>대표적으로</p>
<p><code>let</code> , <code>run</code> , <code>with</code> , <code>apply</code> , <code>also</code>이렇게 5개의 Scope 함수가 있음</p>
<h3 id="1-let">1. <code>let</code></h3>
<p>null이 아닌 경우에 사용하고 그에 따른 새로운 결과를 반환하고 싶을 때 사용함. 또한 lambda함수처럼 마지막줄의 값이 return됨. let을 중첩해서 사용하면 가독성이 별로 좋지 않아서 중첩할 일이 있으면, if else구문을 사용하는 것을 추천한다. it으로 객체에 접근하게 됨.</p>
<pre><code class="language-kotlin">val str: String? = &quot;null&quot;

val result: Int? = str?.let {
    println(it)
    val abc: String? = &quot;abc&quot;
    val def: String? = &quot;def&quot;
    if (!abc.isNullOrEmpty() &amp;&amp; !def.isNullOrEmpty()) {
        println(&quot;abcdef가 null이 아님&quot;)
    }
    1234
}
println(result) // 1234

val str: String? = &quot;null&quot;
val abc: String? = &quot;abc&quot;
val def: String? = &quot;def&quot;

val result2: Int? =  str?.let {
        println(it)
        abc?.let {
                println(it)
                def?.let {
                        println(it)
                        }
                }
        1234
        }
println(result2) // 1234</code></pre>
<h3 id="2-run">2. <code>run</code></h3>
<p>일반적으로 class를 사용할 때 다음과 같이 짜게 됨.</p>
<pre><code class="language-kotlin">fun main() {
    val config = DatabaseClient()
    config.url = &quot;localhost:3306&quot;
    config.username = &quot;mysql&quot;
    config.password = &quot;1234&quot;
    val contected = config.connect()
    println(contected)
    // DB 접속 중...
    // DB 접속 완료
}

class DatabaseClient {

    var url: String? = null
    var username: String? = null
    var password: String? = null

    // DB에 접속하고 Boolean 결과를 반환
    fun connect(): Boolean {
        println(&quot;DB 접속 중...&quot;)
        Thread.sleep(1000)
        println(&quot;DB 접속 완료&quot;)
        return true
    }
}
</code></pre>
<p>위 코드를 run을 활용하여 바꾸면 아래와 같음. 중복되는 코드 없이 바로바로 사용할 수 있는 것임. 아래 코드 역시 let을 통해서 코드를 작성할 수 있으나 it이 중복되므로, let보다는 run을 이용하여 만드는 것이 훨씬 좋다. this를 통해 객체 property에 접근하게 됨.</p>
<pre><code class="language-kotlin">fun main() {
    val connected: Boolean = DatabaseClient().run {
    url = &quot;localhost:3306&quot;
    username = &quot;mysql&quot;
    password = &quot;1234&quot;
    connect()
    }
    println(connected)
    // DB 접속 중...
    // DB 접속 완료
}
</code></pre>
<h3 id="3-with">3. <code>with</code></h3>
<p>with는 결과 반환없이 내부에서 수신객체를 이용할 때 사용함.  이 역시 lambda함수 이기 때문에 마지막 줄이 return값.</p>
<pre><code class="language-kotlin">fun main() {
    val withEx = &quot;안녕하세요&quot;
    val length = with(withEx) {
        length
    }
    println(length) // 5

    val connectedUsingWith = with(DatabaseClient()) {
        url = &quot;localhost:3306&quot;
        username = &quot;mysql&quot;
        password = &quot;1234&quot;
        connect()
    }
    println(connectedUsingWith) // DatabaseClient@372f7a8d

}</code></pre>
<h3 id="4-apply">4. <code>apply</code></h3>
<p>apply는 수신객체의 property를 구성하고 그 수신객체를 그대로 반환하고 싶을 때 사용한다. this로 사용</p>
<pre><code class="language-kotlin">val connectedUsingApply: DatabaseClient = DatabaseClient().apply {
        url = &quot;localhost:3306&quot;
        username = &quot;mysql&quot;
        password = &quot;1234&quot;
        connect()
    }
println(connectedUsingApply)</code></pre>
<h3 id="5-also">5. <code>also</code></h3>
<p>also는 객체 그 자체의 동작을 제어하고 싶을 때 사용할 수 있음</p>
<pre><code class="language-kotlin">class User(val name: String, val password: String) {
    fun validate() {
        if (name.isNotEmpty() &amp;&amp; password.isNotEmpty()) {
            println(&quot;검증성공&quot;)
        } else {
            println(&quot;검증실패&quot;)
        }
    }
    fun printName() = println(name)
}

fun main() {

    val user: User = User(name = &quot;tony&quot;, password = &quot;1234&quot;)
    user.validate()

    User(name = &quot;tony&quot;, password = &quot;1234&quot;).also {
        it.validate() // &quot;검증성공&quot;
        it.printName() // &quot;검증실패&quot;
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[회고] 비전공자 백준 플래티넘 달성 후기]]></title>
            <link>https://velog.io/@dev-junku/%ED%9A%8C%EA%B3%A0-%EB%B9%84%EC%A0%84%EA%B3%B5%EC%9E%90-%EB%B0%B1%EC%A4%80-%ED%94%8C%EB%9E%98%ED%8B%B0%EB%84%98-%EB%8B%AC%EC%84%B1-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@dev-junku/%ED%9A%8C%EA%B3%A0-%EB%B9%84%EC%A0%84%EA%B3%B5%EC%9E%90-%EB%B0%B1%EC%A4%80-%ED%94%8C%EB%9E%98%ED%8B%B0%EB%84%98-%EB%8B%AC%EC%84%B1-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Mon, 30 May 2022 06:28:42 GMT</pubDate>
            <description><![CDATA[<h1 id="백준-플래티넘-달성-후기">백준 플래티넘 달성 후기</h1>
<p>개발을 본격적으로 학습하기 시작한지 어어언~ 1년 하고도 6개월이 흘러갔다. 그동안 나름 성실히 알고리즘 문제를 풀기도 했다. 특히 취준을 진심으로 시작한 근 6개월 동안 매일 매일 풀었던 것 같다. 주말에는 좀 쉰다고 해도.. 주중에는 나름 열심히 풀었다. 그렇게 풀었던 백준 문제는 350여개 정도.. 생각보다 그렇게 많지는 않았다.</p>
<p>물론 programmers나 swea까지 포함한다면 거의 700문제 가량 풀었지만 백준만 놓고 본다면,</p>
<blockquote>
<p><code>플래</code>가 과연 달성하기 어려운 티어인가?</p>
</blockquote>
<p>라는 생각을 하게 된다.</p>
<p>그 누구나 꾸준히 문제를 푼다면 플래에 갈 수 있지만, 그 꾸준함을 증명하기 어렵다고 생각했기에 플래를 달성한 오늘 기분이 매우 좋다ㅋㅋㅋㅋ</p>
<p>앞으로도 알고리즘은 꾸준히 하겠지만, 몇가지 바램이 생겼다.</p>
<ol>
<li>C++을 배우자.</li>
</ol>
<ul>
<li>Python 한정 약간의 한계를 맛본거 같다. (개인적으로 다이아 전까지는 충분히 파이썬으로도 많은 문제를 풀 수 있지만 미래를 위해 C++ 문법을 배울만 하다.)</li>
</ul>
<ol start="2">
<li>최대한 다양한 언어로 풀자.</li>
</ol>
<ul>
<li>kotlin, Python으로 대부분의 문제를 풀었으나, C++, Java로도 푸는 연습을 해야겠다.</li>
</ul>
<ol start="3">
<li>플래는 시작일뿐 <code>다이아</code>가 있다.</li>
</ol>
<ul>
<li>멈추지 말고 <code>다이아</code>에 도전에보자. 갈 수 있을지는 모르겠지만,, 진짜 랭커는 어떨지 궁금하다.</li>
</ul>
<p>오늘은 편히 잘듯?ㅋㅋ</p>
<p><img src="https://velog.velcdn.com/images/dev-junku/post/a8031976-58d2-4420-85a5-7532f6beae83/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BOJ] 그림 교환 in Python]]></title>
            <link>https://velog.io/@dev-junku/BOJ-%EA%B7%B8%EB%A6%BC-%EA%B5%90%ED%99%98-in-Python</link>
            <guid>https://velog.io/@dev-junku/BOJ-%EA%B7%B8%EB%A6%BC-%EA%B5%90%ED%99%98-in-Python</guid>
            <pubDate>Mon, 30 May 2022 04:58:22 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/1029">문제 풀러 가기!</a></p>
<p>일단 전 이거 틀린게 맞는거 같습니다. 맞았습니다! 라고 떴지만, 문제에서 유도한 제약사항을 간신히 통과했고 문제에서 요구한 코드를 작성하지 않았기 때문에 틀린게 맞는거 같습니다.</p>
<p>ㅠㅠ</p>
<p>여튼 제가 풀이한 코드는 다음과 같습니다. 원래라면 틀려야되기 때문에</p>
<p>코드 설명은 안하겠습니다 ㅜㅜ</p>
<h5 id="내-코드">내 코드</h5>
<pre><code class="language-python">from collections import deque
import sys
input = sys.stdin.readline

n = int(input())
price = [list(input().strip()) for _ in range(n)]
state = 1
answer = 1
is_buy = [[[1 for _ in range(n)] for _ in range(n)] for _ in range(1 &lt;&lt; (n+1))]


def bfs(state, p, now):
    global answer

    q = deque([(state, p, now, 1)])

    while q:
        state, p, now, cnt = q.popleft()

        if answer &lt; cnt: answer = max(answer, cnt)

        for nxt in range(n):
            if state &amp; (1 &lt;&lt; nxt): continue
            if p &gt; int(price[now][nxt]): continue
            if is_buy[state][now][nxt] &gt;= cnt+1:
                continue

            is_buy[state][now][nxt] = cnt+1
            q.append((state | (1 &lt;&lt; nxt), int(price[now][nxt]), nxt, cnt + 1))

bfs(1, 0, 0)
print(answer)</code></pre>
<p>단순하게 그냥 bfs 돌렸는데.. 음.. 시간이 너무 오래걸렸습니다..ㅋㅋㅋㅋㅋ 아직도 많이 부족하네요 ㅜㅜ</p>
<p>몇몇분들은 bfs로 돌린것을 알고 있으나, 제 bfs는 너무 비효율적이네요..ㅋㅋ</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BOJ] 말이 되고픈 원숭이 in Python]]></title>
            <link>https://velog.io/@dev-junku/BOJ-%EB%A7%90%EC%9D%B4-%EB%90%98%EA%B3%A0%ED%94%88-%EC%9B%90%EC%88%AD%EC%9D%B4-in-Python</link>
            <guid>https://velog.io/@dev-junku/BOJ-%EB%A7%90%EC%9D%B4-%EB%90%98%EA%B3%A0%ED%94%88-%EC%9B%90%EC%88%AD%EC%9D%B4-in-Python</guid>
            <pubDate>Fri, 27 May 2022 07:15:12 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/1600">문제 풀러 가기!</a></p>
<p>이 문제 처음 읽고 일반환된 코드를 생각하지 못해 틀렸습니다.</p>
<p>중요한 것은 문제에서 이야기한 말 이동의 횟수에 따라 이동할 수 있는 것이 달라진다는 것. 그리고 이걸 방문 배열에서 어떻게 체크할 것인가? 입니다. 만약에 자꾸 틀리신다면, 아래 TEST CASE를 분석해보시면, 느낌이 오실겁니다.. 일반적인 2차원 방문 배열로는 이 문제를 풀 수 없구나..</p>
<pre><code>1
5 5
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 1 1
0 0 0 1 0</code></pre><p>즉, 낚시 문제였던 것 같습니다. 다행이 k값을 고려하여 방문 배열을 3차원으로 늘려줬더니 풀리게되었습니다. 물론 Python은 6600ms, PyPy는 600ms로 통과되었는데, Python은 참.. 정말 느린 언어네요.. 조금씩 Kotlin과 C++로 언어를 옮겨야할 것 같습니다..</p>
<h5 id="문제풀이">문제풀이</h5>
<p>일단 문제의 조건을 준수합시다.</p>
<p>원숭이는 말처럼 이동 할수도 있고 그냥 <code>상 하 좌 우</code> 격자 이동만 할 수 있습니다. 그리고 말처럼 이동하는 것은 제약 사항이 있습니다. 바로 k번만 이동할 수 있다는 것이죠. 따라서 이 모두를 고려 해야 합니다.</p>
<blockquote>
<p>그러면 어떤 이동이 우선이 되어야 할까요?</p>
</blockquote>
<p>제 생각이지만 <code>상 하 좌 우</code> 격자 이동이 우선시 되어야 할 것 같습니다. 그 이유는 위에서 제시된 Test Case에서는 <code>상 하 좌 우</code>로만 이동하다가 마지막에 말 점프를 합니다. 말 점프를 먼저하게 되면, 방금 말씀드린 경로를 찾을 수 없게 됩니다. 따라서 격자 이동을 먼저 하고 말 이동을 하는 것이 모든 경로를 고려할 수 있다고 생각했습니다.</p>
<p>그러면 방문 배열</p>
<p>1 차원: 말이동을 몇번 했는지
2 차원: 행
3 차원: 열</p>
<p>로 구성하여 BFS 탐색을 시도한다면 도착 지점까지의 가장 빠른 경로를 탐색할 수 있습니다.</p>
<p>따라서 전체 코드는 아래와 같이 구성됩니다.</p>
<h5 id="전체-코드">전체 코드</h5>
<pre><code class="language-python">from collections import deque
import sys
input = sys.stdin.readline

k = int(input())
w, h = map(int, input().split())

INF = int(1e9)

arr = [list(map(int, input().split())) for _ in range(h)]
visited = [[[INF for _ in range(w)] for _ in range(h)] for _ in range(k+1)]
delta = [(0, 1), (1, 0), (0, -1), (-1, 0)]
horse_delta = [
    (-1, -2),
    (-2, -1),
    (-1, 2),
    (-2, 1),
    (1, 2),
    (2, 1),
    (2, -1),
    (1, -2)
]

sx, sy = 0, 0
ex, ey = h-1, w-1
def bfs(sx, sy):

    q = deque([(sx, sy, 0, 0)])

    while q:
        x, y, cnt, c = q.popleft()

        if x == ex and y == ey:
            return cnt

        for i in range(4):
            nx, ny = x + delta[i][0], y + delta[i][1]

            if not (0 &lt;= nx &lt; h and 0 &lt;= ny &lt; w): continue
            if arr[nx][ny] == 1: continue
            if visited[c][nx][ny] != INF : continue

            visited[c][nx][ny] = cnt + 1
            q.append((nx, ny, cnt + 1, c))

        if c &gt;= k: continue

        for i in range(8):
            nx, ny = x + horse_delta[i][0], y + horse_delta[i][1]

            if not (0 &lt;= nx &lt; h and 0 &lt;= ny &lt; w): continue
            if arr[nx][ny] == 1: continue
            if visited[c+1][nx][ny] != INF: continue

            visited[c+1][nx][ny] = cnt + 1
            q.append((nx, ny, cnt + 1, c + 1))

    return -1

print(bfs(sx, sy))</code></pre>
<p>질문과 오타 수정 언제나 환영입니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BOJ] 달이 차오른다 가자 in Python]]></title>
            <link>https://velog.io/@dev-junku/BOJ-%EB%8B%AC%EC%9D%B4-%EC%B0%A8%EC%98%A4%EB%A5%B8%EB%8B%A4-%EA%B0%80%EC%9E%90-in-Python</link>
            <guid>https://velog.io/@dev-junku/BOJ-%EB%8B%AC%EC%9D%B4-%EC%B0%A8%EC%98%A4%EB%A5%B8%EB%8B%A4-%EA%B0%80%EC%9E%90-in-Python</guid>
            <pubDate>Wed, 25 May 2022 06:40:58 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/1194">문제 풀러 가기!</a></p>
<p>문제의 조건을 준수하면서 그래도 코드에 입히면 끝나는 문제였으나, 그게 쉬웠으면 Gold1에 랭크되어 있진 않겠지요..</p>
<p>요점은 문제의 조건을 어떻게 코드에 입힐 것인가? 입니다.
이때는 비트 마스크를 활용합니다.</p>
<blockquote>
<p>비트마스크를 사용한 다는 것은 결국 <code>어떤 상태</code>를 <code>기록</code>하겠다는 의미거든요.</p>
</blockquote>
<p>문제에는 어떤 상태가 존재할까요?</p>
<p>바로 민식이가 바로 어떤 키를 갖고 있는가? 입니다.</p>
<p>민식이가 갖고 있는 키값에 따라 갈 수 있는 경로가 달라지기 때문에 어떤 키를 갖고 있는지 여부를 bit로 구분하면 됩니다.</p>
<p>예를 들어서 문제에서 등장한 6개 키인 a<del>f를 1</del>6으로 대칭시켜보죠.</p>
<p>각각에 해당되는 key를 갖고 있다고 가정하면 다음과 같습니다.</p>
<table>
<thead>
<tr>
<th>f = 6</th>
<th>e = 5</th>
<th>d = 4</th>
<th>c = 3</th>
<th>b = 2</th>
<th>a = 1</th>
</tr>
</thead>
<tbody><tr>
<td>100000</td>
<td>010000</td>
<td>001000</td>
<td>000100</td>
<td>000010</td>
<td>000001</td>
</tr>
</tbody></table>
<p>이걸 6개의 모든 비트(2의 6제곱)에 대해 모든 방문 배열을 만듭니다. 이 방문 배열로 탐색할겁니다.</p>
<p>그리고 위의 근거로 인해 방문 배열은 3차원입니다.</p>
<p>1차원 → 갖고 있는 키 (state)
2차원 → 행
3차원 → 열</p>
<p>그러면 이제 이걸 코드로 반영해주어야되기 때문에 다음과 같은 코드로 미리 세팅해줍시다.</p>
<pre><code class="language-python">get_key = {
    &quot;a&quot;: 0,
    &quot;b&quot;: 1,
    &quot;c&quot;: 2,
    &quot;d&quot;: 3,
    &quot;e&quot;: 4,
    &quot;f&quot;: 5
}

use_key = {
    &quot;A&quot;: 0,
    &quot;B&quot;: 1,
    &quot;C&quot;: 2,
    &quot;D&quot;: 3,
    &quot;E&quot;: 4,
    &quot;F&quot;: 5
}

key = [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;d&quot;, &quot;e&quot;, &quot;f&quot;]
set_key = set()
set_use_key = set()


for k in key:
    set_key.add(k)
    set_use_key.add(k.upper())</code></pre>
<p>미로를 순회하다가 키값을 얻으면 바로 접근할 수 있도록 하기 위해서 get_key로 그리고 key를 사용할때는 대문자이기 때문에 use_key로 해당 dictionary를 사전에 세팅해주었습니다.</p>
<p>또한 6개의 key를 배열로 두지 않고 set으로 만들었습니다. 시간복잡도를 최대한 줄이기 위해서입니다. set으로 하면 제가 알기로 시간복잡도를 더 줄일 수 있는 것으로 알고 있습니다. 왜요? 라고 말씀하신다면.. 그냥 제가 문제를 풀어보면서 set으로 했을 때 시간 복잡도가 많이 줄어든 경험이 있어서 그런거 같습니다. 사실 여기서 더 설명하면 참조 지역성을 설명해야 되는데, 저 set은 아마 cpu에서 참조 지역성이 좁은 곳에 저장하지 않을까 생각드네요.</p>
<p>여튼 이제 키값을 갖는 state에 따라서 방문 여부를 체크할 visited 배열을 만들어줍시다.</p>
<pre><code class="language-python"># 델타이동
dx = [0, 1, 0, -1]
dy = [1, 0, -1, 0]

# 데이터 받기
n, m = map(int, input().split())
maze = [list(input().strip()) for _ in range(n)]

# 시작점 찾기
sx, sy = -1, -1
for i in range(n):
    for j in range(m):
        if maze[i][j] == &quot;0&quot;:
            sx, sy = i, j
            break
    if sx != -1 and sy != -1:
        break

# 방문 배열 만들기
visited = [[[False for _ in range(m)] for _ in range(n)] for _ in range((1 &lt;&lt; 6) + 1)]</code></pre>
<p>그리고 탐색을 시도합시다</p>
<pre><code class="language-python">def find_exit(state, x, y, cnt):

    answer = int(10e9)
    q = deque([])
    q.append((state, x, y, cnt))

    while q:

        state, x, y, cnt = q.popleft()

        # 여기서 문제의 답을 도출할 것임
        if maze[x][y] == &quot;1&quot;:
            answer = min(answer, cnt)

        for i in range(4):
            # 다음 좌표
            nx, ny = x + dx[i], y + dy[i]

            # 범위에 벗어나거나 이미 해당 상태에서 방문 또는 벽이면 볼 필요없음
            if not (0 &lt;= nx &lt; n and 0 &lt;= ny &lt; m): continue
            if visited[state][nx][ny] or maze[nx][ny] == &quot;#&quot;: continue

            # 민식이가 갈 수 있는 좌표임 열쇠를 얻은 것은 아니기 때문에 state가 바뀌지는 않음
            if (maze[nx][ny] == &quot;.&quot; or maze[nx][ny] == &quot;0&quot; or maze[nx][ny] == &quot;1&quot;) and not visited[state][nx][ny]:
                visited[state][nx][ny] = True
                q.append((state, nx, ny, cnt+1))

            # 민식이가 key를 얻었으므로 state를 변경해주어 q에 넣어줌
            elif maze[nx][ny] in set_key and not visited[state | (1 &lt;&lt; get_key[maze[nx][ny]])][nx][ny]:
                visited[state | (1 &lt;&lt; get_key[maze[nx][ny]])][nx][ny] = True
                q.append((state | (1 &lt;&lt; get_key[maze[nx][ny]]), nx, ny, cnt+1))

            # 민식이가 갖고 있는 키의 상태로 갈 수 있음 주의할 것은 key의 상태는 바뀌지 않음. 만약에 키가 한 번쓰고 끝나는 문제라면 해당 부분을 0으로 바꿔주면 됨. 어떻게? xor 연산으로..
            elif maze[nx][ny] in set_use_key and state &amp; (1 &lt;&lt; use_key[maze[nx][ny]]):
                visited[state][nx][ny] = True
                q.append((state, nx, ny, cnt+1))

    return answer

ans = find_exit(0, sx, sy, 0)</code></pre>
<p>처음에는 아무런 key도 갖고 있지 않기 때문에 state는 0입니다.
cnt는 이동한 경로를 세어줄겁니다.</p>
<p>이제 답을 도출합니다.</p>
<pre><code class="language-python">if ans == int(10e9):
    print(-1)
else:
    print(ans)</code></pre>
<h5 id="전체-코드">전체 코드</h5>
<pre><code class="language-python">from collections import deque
import sys
input = sys.stdin.readline

get_key = {
    &quot;a&quot;: 0,
    &quot;b&quot;: 1,
    &quot;c&quot;: 2,
    &quot;d&quot;: 3,
    &quot;e&quot;: 4,
    &quot;f&quot;: 5
}

use_key = {
    &quot;A&quot;: 0,
    &quot;B&quot;: 1,
    &quot;C&quot;: 2,
    &quot;D&quot;: 3,
    &quot;E&quot;: 4,
    &quot;F&quot;: 5
}

key = [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;d&quot;, &quot;e&quot;, &quot;f&quot;]
set_key = set()
set_use_key = set()


for k in key:
    set_key.add(k)
    set_use_key.add(k.upper())

dx = [0, 1, 0, -1]
dy = [1, 0, -1, 0]

n, m = map(int, input().split())
maze = [list(input().strip()) for _ in range(n)]

sx, sy = -1, -1
for i in range(n):
    for j in range(m):
        if maze[i][j] == &quot;0&quot;:
            sx, sy = i, j
            break
    if sx != -1 and sy != -1:
        break

visited = [[[False for _ in range(m)] for _ in range(n)] for _ in range((1 &lt;&lt; 6) + 1)]


def find_exit(state, x, y, cnt):
    answer = int(10e9)
    q = deque([])

    q.append((state, x, y, cnt))
    while q:
        state, x, y, cnt = q.popleft()
        if maze[x][y] == &quot;1&quot;:
            answer = min(answer, cnt)

        for i in range(4):
            nx, ny = x + dx[i], y + dy[i]
            if not (0 &lt;= nx &lt; n and 0 &lt;= ny &lt; m): continue
            if visited[state][nx][ny] or maze[nx][ny] == &quot;#&quot;: continue

            if (maze[nx][ny] == &quot;.&quot; or maze[nx][ny] == &quot;0&quot; or maze[nx][ny] == &quot;1&quot;) and not visited[state][nx][ny]:
                visited[state][nx][ny] = True
                q.append((state, nx, ny, cnt+1))

            elif maze[nx][ny] in set_key and not visited[state | (1 &lt;&lt; get_key[maze[nx][ny]])][nx][ny]:
                visited[state | (1 &lt;&lt; get_key[maze[nx][ny]])][nx][ny] = True
                q.append((state | (1 &lt;&lt; get_key[maze[nx][ny]]), nx, ny, cnt+1))

            elif maze[nx][ny] in set_use_key and state &amp; (1 &lt;&lt; use_key[maze[nx][ny]]):
                visited[state][nx][ny] = True
                q.append((state, nx, ny, cnt+1))

    return answer

ans = find_exit(0, sx, sy, 0)

if ans == int(10e9):
    print(-1)
else:
    print(ans)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BOJ] 빵집 in Python]]></title>
            <link>https://velog.io/@dev-junku/BOJ-%EB%B9%B5%EC%A7%91-in-Python</link>
            <guid>https://velog.io/@dev-junku/BOJ-%EB%B9%B5%EC%A7%91-in-Python</guid>
            <pubDate>Tue, 24 May 2022 04:46:25 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/3109">문제 풀러 가기!</a></p>
<p>문제를 읽다보면, 가장 위쪽 방향부터 찾으면 되겠다고 생각드는 문제였다.(이부분이 Greedy다)</p>
<p>사실 내 풀이는 최악의 시간복잡도가 1초 1억이 넘는다. Python이기 때문에..... 핸디캡이 붙은 듯 하다.</p>
<p>여튼 가장 위쪽 부터 탐색을 시도하면 그 다음 파이프라인은 이전에 만들어 놓은 파이프 라인 때문에 그 선으로 절대 갈 수 없으므로 이를 이용해서 DFS 탐색을 진행하면 된다. 이때 문제에서 요구한 c번째 열에 도착하면 파이프가 이어진 것이므로 이를 통해 파이프의 개수를 세면 되겠다.</p>
<p>DFS 알고리즘을 어떻게 짤 수 있나 생각이 들 수 있겠지만, toggle로 True False로 만든 뒤에 이를 계속 그 전 깊이로 넘기면 되겠다.</p>
<h5 id="전체-코드">전체 코드</h5>
<pre><code class="language-python">import sys
input = sys.stdin.readline

r, c = map(int, input().split())
board = [list(input().strip()) for _ in range(r)]
visited = [[False for _ in range(c)] for _ in range(r)]

dx = [-1, 0, 1]
dy = [1, 1, 1]

def dfs(x, y):

    if y == c-1:
        return True

    toggle = False
    for i in range(3):
        nx, ny = x + dx[i], y + dy[i]
        if not (0 &lt;= nx &lt; r and 0 &lt;= ny &lt; c): continue
        if visited[nx][ny]: continue
        if board[nx][ny] == &quot;.&quot; and not toggle:
            visited[nx][ny] = True
            toggle = dfs(nx, ny)

    return toggle

answer = 0
for i in range(r):
    visited[i][0] = True
    if dfs(i, 0):
        answer += 1

print(answer)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BOJ] 캠프준비 in Python]]></title>
            <link>https://velog.io/@dev-junku/BOJ-%EC%BA%A0%ED%94%84%EC%A4%80%EB%B9%84-in-Python</link>
            <guid>https://velog.io/@dev-junku/BOJ-%EC%BA%A0%ED%94%84%EC%A4%80%EB%B9%84-in-Python</guid>
            <pubDate>Fri, 20 May 2022 03:48:48 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/16938">문제 풀러 가기!</a></p>
<p>난이도: G5
사용한 알고리즘: Bitmask, 완전 탐색</p>
<p>G5에 랭크되어 있어 어느 정도 어렵겠지 했지만, 그렇지 않았습니다. 1일 알고리즘.. 이거로 힐링하고 있는데, 만족스럽지 않네요ㅜ</p>
<p>점점 골드가 골드가 아닌거 같은 이 느낌....</p>
<p>그래도 겸손하게 꾸준히 풀어야겠습니다.</p>
<h5 id="문제-설명">문제 설명</h5>
<p>그냥 문제를 선택하는 것을 bitmask로 결정하면 됩니다.</p>
<p>하지만 문제의 조건 상 문제를 2개 이상을 고르는 것이 약간 마음에 걸리네요.</p>
<p>물론 그러한 조건을 코드에 추가하지 않아도 통과가 되었으나, 좀 더 정확한 코드이기 위해서는 앞서 말한 조건을 추가시키는 것이 좋겠네요.</p>
<p>여튼 1이면 문제를 선택! 그렇지 않으면 선택하지 않는 것으로 생각했고 이를 순회하면서 문제의 조건에 충족되는 것만 count 해주면 끝납니다.</p>
<p>그러면 bitmask를 할 수 있는 이유는 n이 15로 값이 생각보다 크지 않기 때문입니다.</p>
<p>최대 15 * 2^15로 50만번 순회 끝에 문제를 풀 수 있습니다.</p>
<h5 id="코드">코드</h5>
<pre><code class="language-python">import sys
input = sys.stdin.readline

n, l, r, x = map(int, input().split())
level = list(map(int, input().split()))

cnt = 0
for i in range(1 &lt;&lt; n):

    c = 0
    for j in range(n):
        if i &amp; (1 &lt;&lt; j):
            c += 1

    if c &lt; 2: continue

    l_s = 0
    min_l = int(10e9)
    max_l = 0
    diff = 0
    for j in range(n):
        if i &amp; (1 &lt;&lt; j):
            l_s += level[j]
            min_l = min(min_l, level[j])
            max_l = max(max_l, level[j])
            diff = max_l - min_l

    if not (l &lt;= l_s &lt;= r):
        continue

    if not x &lt;= diff:
        continue

    cnt += 1

print(cnt)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android] Error running 'Preview': "There is no @Composable named]]></title>
            <link>https://velog.io/@dev-junku/Android-Error-running-Preview-There-is-no-Composable-named</link>
            <guid>https://velog.io/@dev-junku/Android-Error-running-Preview-There-is-no-Composable-named</guid>
            <pubDate>Thu, 19 May 2022 14:05:12 GMT</pubDate>
            <description><![CDATA[<p><strong>안드로이드 공부 기록</strong></p>
<p>compose ui를 이용해서 실습하다가 </p>
<p><strong>Error running &#39;Preview&#39;: &quot;There is no @Composable named</strong></p>
<p>라는 에러가 발생했다.</p>
<p>뭐지했다. 왜냐하면 나는 @preview를 지웠기 때문이다.</p>
<p>한참을 찾다가 안드로이드 스튜디오 상단에 있는 버튼을 보았다.</p>
<p>바로 이렇게 생긴..</p>
<p><img src="https://velog.velcdn.com/images/dev-junku/post/01091d70-82be-43cb-8a93-10955573e3d8/image.png" alt=""></p>
<h1 id="">⁇⁇⁇⁇⁇⁇⁇⁇⁇⁇⁇⁇</h1>
<p><img src="https://velog.velcdn.com/images/dev-junku/post/25ea32e2-03eb-4726-8bf5-4db7f3c5a447/image.png" alt=""></p>
<h1 id="-1">⁇⁇⁇⁇⁇⁇⁇⁇⁇⁇⁇⁇</h1>
<p><img src="https://velog.velcdn.com/images/dev-junku/post/120f9e33-5b1b-40d4-98ce-0eb48b801a7f/image.png" alt=""></p>
<h1 id="ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ">ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ</h1>
<p>그렇다.. 내 잘못이었다.. 저거 잘봐야겠다.. preview를 사용안하고 그냥 바로 App을 build하고 싶으면 저 위부터 찾아보자...</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BOJ] 게리맨더링 in Python]]></title>
            <link>https://velog.io/@dev-junku/BOJ-%EA%B2%8C%EB%A6%AC%EB%A7%A8%EB%8D%94%EB%A7%81-in-Python</link>
            <guid>https://velog.io/@dev-junku/BOJ-%EA%B2%8C%EB%A6%AC%EB%A7%A8%EB%8D%94%EB%A7%81-in-Python</guid>
            <pubDate>Wed, 18 May 2022 07:22:54 GMT</pubDate>
            <description><![CDATA[<h3 id="의식의-흐름">의식의 흐름</h3>
<p>예전에 풀어본 문제지만, 그때는 안풀려서 다른 분 답을 참고했다. bitmask를 활용한 알고리즘 문제를 꼭 내 힘으로 풀어보겠다는 다짐을 한체 꾸준히 bitmask를 풀었고 어느 정도 감이 잡혔던 상태로 다시 한 번 이 문제에 도전했다. 문제는 그렇게 어렵지 않다. 하지만 이것을 어떻게 bitmask로 옮길 것인가? 는 좀 다른 질문인것 같다.</p>
<p>여튼 문제를 꼼꼼히 읽고 어떻게 해야 할지 설계를 했다.</p>
<p>문제의 노드의 개수는 10개로 입력값이 많지 않다. 따라서 bitmask를 활용한 완전 탐색을 진행하는 것이 효율적이다.</p>
<ol>
<li>일단 각각의 노드가 존재한다는 점</li>
<li>각각의 노드로 2개의 집단을 나눴을 때 꼭 집단 안에서 모든 노드를 방문할 수 있어야 한다는 점</li>
<li>모든 노드는 집단에 있어야 한다는 점</li>
<li>각 집단의 state를 bitmask로 나눌 수 있다는 점</li>
<li>문제에서 주어진 데이터는 graph 형식으로 다룰 수 있다는 점</li>
</ol>
<p>그래서 일단 데이터를 받고 각각의 상태를 구분했다.</p>
<p>이를 위해 모든 노드를 방문하는 상태를 만들고 특정 상태를 만들고 이에 반대되는 상태를 만들었다. 그게 아래의 코드다.</p>
<pre><code class="language-python"># 모든 노드를 방문하는 상태
total_state = 1
for i in range(n):
    total_state = total_state | (1 &lt;&lt; i)

# group1의 상태와 group2의 상태
for i in range(1 &lt;&lt; n):
    state1 = i
    state2 = i^total_state</code></pre>
<p>위 코드를 통해 각각의 상태를 구분했으며, 이 상태들을 통해 방문 여부를 체크해야 한다.</p>
<p>나는 DFS를 이용해서 방문했고, BFS로 방문해도 상관없다. 본인이 편한 탐색 알고리즘을 사용하면 된다.</p>
<h4 id="dfs">DFS</h4>
<pre><code class="language-python">def dfs(start, state):
    for nxt in graph[start]:
        if not visited[nxt] and (1 &lt;&lt; nxt-1) &amp; state:
            visited[nxt] = True
            dfs(nxt, state)</code></pre>
<p>참고로 다음 노드를 의미하는 nxt에 -1을 한 이유는 1번 노드는 <code>1 &lt;&lt; 0</code> 이고 2번 노드는 <code>1 &lt;&lt; 1</code>을 의미하기 때문이다.</p>
<p>여튼 위 dfs를 이용하여 다음과 같이 group1, 2의 상태에 방문 여부를 체크한다.</p>
<h4 id="방문-로직">방문 로직</h4>
<pre><code class="language-python">    for j in range(n):
        if state1 &amp; (1 &lt;&lt; j) and not visited[j]:
            visited[j+1] = True
            dfs(j+1, state1)
            break

    for j in range(n):
        if state2 &amp; (1 &lt;&lt; j) and not visited[j]:
            visited[j+1] = True
            dfs(j+1, state2)
            break</code></pre>
<p>솔직히 같은 코드를 작성해서 별로 마음에 들진 않지만, 2개의 집단 밖에 없기 때문에 충분히 문제에 알맞은 코드로 판단했다.</p>
<p>group의 상태를 와 각 비트를 체크하면서 <code>1 &lt;&lt; j</code>가 state에 속하면서 방문하지 않은 노드라면 dfs 순회를 시도하되, 한 번 순회하면 끝내라는 의미이다. 문제의 조건에서 모든 그룹의 노드는 단 한 번의 순회로 모두 방문할 수 있기 때문에 이렇게 진행했다.</p>
<p>그리고 이렇게 순회했을 때 visited는 모두 방문체크가 되어 있어야 한다.</p>
<h4 id="방문-체크">방문 체크</h4>
<pre><code class="language-python">    for j in range(1, n+1):
        if not visited[j]:
            break</code></pre>
<p>만약 위 code에서 break 문제 걸리지 않았다면, 문제에서 주어진 조건을 만족하는 state이기 때문에 다음과 같은 연산을 시도한다.</p>
<h4 id="답-도출">답 도출</h4>
<pre><code class="language-python">    p1 = 0
    p2 = 0

    for k in range(n):
        if (1 &lt;&lt; k) &amp; state1:
            p1 += populations[k]
        else:
            p2 += populations[k]

        answer = min(answer, abs(p1-p2))</code></pre>
<h3 id="전체-코드">전체 코드</h3>
<pre><code class="language-python">import sys
input = sys.stdin.readline

n = int(input())
populations = list(map(int, input().split()))

graph = [[] for _ in range(n+1)]
for i in range(1, n+1):
    data = list(map(int, input().split()))
    for j in range(data[0]):
        graph[i].append(data[j+1])

def dfs(start, state):
    for nxt in graph[start]:
        if not visited[nxt] and (1 &lt;&lt; nxt-1) &amp; state:
            visited[nxt] = True
            dfs(nxt, state)

total_state = 1
for i in range(n):
    total_state = total_state | (1 &lt;&lt; i)

answer = 1001
for i in range(1 &lt;&lt; n):

    state1 = i
    state2 = i^total_state

    visited = [False for _ in range(n+1)]

    for j in range(n):
        if state1 &amp; (1 &lt;&lt; j) and not visited[j]:
            visited[j+1] = True
            dfs(j+1, state1)
            break

    for j in range(n):
        if state2 &amp; (1 &lt;&lt; j) and not visited[j]:
            visited[j+1] = True
            dfs(j+1, state2)
            break

    for j in range(1, n+1):
        if not visited[j]:
            break
    else:
        p1 = 0
        p2 = 0

        for k in range(n):
            if (1 &lt;&lt; k) &amp; state1:
                p1 += populations[k]
            else:
                p2 += populations[k]

        answer = min(answer, abs(p1-p2))

if answer == 1001:
    print(-1)
else:
    print(answer)</code></pre>
<h3 id="느낀점">느낀점</h3>
<p>처음은 아니지만, 못풀었던 문제를 bitmask로 풀어서 너무 기분이 좋았다. 뭔가 더 성장했다는 느낌을 받았다. 앞으로도 쭈욱 계속 진행해보자!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BOJ] 양 구출 작전 in Python]]></title>
            <link>https://velog.io/@dev-junku/BOJ-%EC%96%91-%EA%B5%AC%EC%B6%9C-%EC%9E%91%EC%A0%84-in-Python</link>
            <guid>https://velog.io/@dev-junku/BOJ-%EC%96%91-%EA%B5%AC%EC%B6%9C-%EC%9E%91%EC%A0%84-in-Python</guid>
            <pubDate>Tue, 17 May 2022 03:43:41 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/16437">문제 풀러 가기!</a></p>
<p><strong>문제 분석</strong>
단순한 DFS 문제로 판단했습니다. 다른 방식은 아직 생각나지 않았습니다.</p>
<p>어차피 문제의 자료구조가 트리구조이기 때문에(순환x) 최하단 자식 노드까지 내려갔다가 부모로 다시 올리면서 문제의 조건을 대입해주면 됩니다.</p>
<p>주의할 점은 문제의 조건상 123,456개의 노드가 존재할 수 있습니다. 그러므로 최대 재귀 깊이를 123,456이상으로 설정해주셔야 합니다. Python은 그렇습니다.</p>
<blockquote>
<p>그러면 이제 순회를 돌아야겠죠?</p>
</blockquote>
<blockquote>
<p>어떻게 순회를 돌 수 있을까요?</p>
</blockquote>
<p>문제의 조건대로 순회를 돌기 위한 데이터 부터 갖춰놓는게 좋겠죠.
단방향에 알맞은 부모와 자식간 연결을 나타내는 graph를 먼저 만들어야 합니다. 그리고 sheep과 wolf 정보를 알아야겠죠. (몇 번 노드에 존재하는지 알아야 합니다.) 문제 설명을 읽어보면, <code>두 번째 줄부터 N-1개에 줄에 2번 섬부터 N번 섬까지 섬의 정보를 나타내는</code>라고 적혀있습니다. 따라서 각 줄이 2번부터 n번 노드까지의 정보를 갖고 있는 데이터임을 알 수 있습니다. 이를 통해 sheep과 wolf를 따로 저장하면, 더할 값과 뺄 값을 따로 관리할 수 있습니다.</p>
<p>결국 알고리즘을 사용하기 전 사전에 준비할 코드는 다음과 같습니다.</p>
<pre><code class="language-python">import sys
sys.setrecursionlimit(10**6)
input = sys.stdin.readline

n = int(input())

graph = [[] for _ in range(n+1)] # tree
s = [0 for _ in range(n+1)] # Sheep
w = [0 for _ in range(n+1)] # Wolf
for i in range(2, n+1): # 나머지 데이터 받기
    sw, number, to = map(str, input().split())

    # 문제의 조건에 따라 sw가 S일 때와 W일 때를 구분해서 따로 값을 저장합니다!
    if sw == &quot;S&quot;: s[i] = int(number)
    else: w[i] = int(number)
    # graph의 경우 단방향으로만 저장합니다.
    # 이때 밑으로 내려가는 과정을 먼저 진행하기 때문에
    # index는 parent, value는 child로 생각하고 저장해주어야 합니다.
    graph[int(to)].append(i)</code></pre>
<p>이를 통해 DFS를 돌리면 되겠죠.</p>
<h4 id="💻-dfs-코드-짜기">💻 DFS 코드 짜기</h4>
<p>가장 먼저 할 것이 최하단 노드까지 내려가 주어야 합니다.</p>
<p><strong>조건 1.</strong> 1번 노드(최상단 노드)에서 graph를 순회하면서 연결된 노드로 쭉쭉 내려가 줍시다!</p>
<p><strong>조건 2.</strong> 최하단 노드까지 내려왔으면, 더 이상 아래로 내려갈 노드가 존재하지 않으므로 DFS를 돌지 않습니다. 그러면 이제 문제에서 주어진 상황을 대입하면서 연산을 수행하여 부모노드로 올려주면 됩니다.</p>
<p><strong>조건 3.</strong> 부모 노드로 올려주다가 최상단 노드로 돌아오면(무조건 sheep, wolf 둘 다 0인 노드) 이를 answer에 더해줍시다.</p>
<p>이러한 방식을 코드로 옮기면 다음과 같습니다.</p>
<pre><code class="language-python">answer = 0
def dfs(start):
    global answer

    # 살아 남은 양을 기록할 number 생성
    number = 0
    # 조건 1. DFS를 돌 때마다 살아 남은 양을 더해주어야 함, 최하단 노드까지 내려가야 함.
    for nxt in graph[start]:
        number += dfs(nxt)

    # 조건 2.
    # 자식에서 부모로 올라올 때 해당 노드가 늑대를 갖고 있으면 늑대의 수만큼 빼주되, 0보다 작거나 같으면 0을 return 함
    if w[start] != 0:
        return number - w[start] if number - w[start] &gt; 0 else 0

    # 자식에서 부모로 올라올 때 해당 노드가 양을 갖고 있으면, number에서 양의 수만큼만 더해서 return 함
    if s[start] != 0:
        return number + s[start]

    # 조건 3.
    # 최상단 노드로 올라왔으면, answer에 number를 더해줌
    if start == 1:
        answer += number
        return</code></pre>
<p>마지막으로 위 문제를 풀기 위한 최종 코드입니다.(python으로 돌리시길 바랍니다. pypy는 메모리 초과가 납니다.)</p>
<pre><code class="language-python">import sys
sys.setrecursionlimit(10**6)
input = sys.stdin.readline

n = int(input())

graph = [[] for _ in range(n+1)]
s = [0 for _ in range(n+1)]
w = [0 for _ in range(n+1)]
for i in range(2, n+1):
    sw, number, to = map(str, input().split())

    if sw == &quot;S&quot;: s[i] = int(number)
    else: w[i] = int(number)

    graph[int(to)].append(i)

answer = 0
def dfs(start):
    global answer

    number = 0
    for nxt in graph[start]:
        number += dfs(nxt)

    if w[start] != 0:
        return number - w[start] if number - w[start] &gt; 0 else 0

    if s[start] != 0:
        return number + s[start]

    if start == 1:
        answer += number
        return

dfs(1)
print(answer)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BOJ] 인터넷 설치 in Python]]></title>
            <link>https://velog.io/@dev-junku/BOJ-%EC%9D%B8%ED%84%B0%EB%84%B7-%EC%84%A4%EC%B9%98-in-Python</link>
            <guid>https://velog.io/@dev-junku/BOJ-%EC%9D%B8%ED%84%B0%EB%84%B7-%EC%84%A4%EC%B9%98-in-Python</guid>
            <pubDate>Mon, 16 May 2022 06:23:24 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/1800">문제 풀러 가기!</a></p>
<p>전형적인 PS 풀이를 이용한 문제입니다.
G1에 랭크되어 있는 만큼 알고리즘 짜기 힘들기도 하고 생각하기도 힘들다는 생각이 드네요.</p>
<p>그래도 괜찮습니다. 차근차큰 생각하다보면, 답이 나올 것입니다.</p>
<p>문제에서 설명하기를 1번은 인터넷이 무조건 연결되어 있고 N번째를 연결하고 싶다고 합니다. 각각의 번호는 모두 노드이고, 노드를 잇기 위한 cost도 존재하고 있습니다.</p>
<p>그리고 사장님이 k개 이상 연결된 간선에 대해서 돈을 지출해야 되는데, 이때 지출할 돈의 최소값을 찾는 문제입니다.</p>
<h4 id="시간-복잡도-생각하기">시간 복잡도 생각하기</h4>
<p>돈의 최소값을 찾아야 하며, 주어진 데이터가 생각보다 많기 때문에(간선 개수만 10,000개, 가중치의 최대값 1,000,000) 이분 탐색을 시도해야 합니다. 그리고 한번 순회를 할 때 최대 10,000개를 모두 돌아다닐 수 있으며 어떤 값이 가장 최적인지 찾기 위해서는 최악으로 1,000,000 * 10,000의 시간복잡도가 나올 수 있습니다. 그러면, 이걸 어떻게 줄일 수 있을까요?</p>
<p>바로 dijkstra입니다. dijkstra로 순회하면 (노드의 개수)*log(간선의 개수) * log(n) → 1000 𝗑 log(10,000) 𝗑 log(1,000,000) 입니다.</p>
<p>즉, 약 1800만 정도의 순회 끝에 모든 순회를 마칠 수 있습니다.</p>
<p>사장이 지출할 값들의 최대값 중 최소값을 찾아야 하므로 이 지출 상한을 이분 탐색으로 찾습니다. 이때 dijkstra로 지출할 값이 target한 값보다 큰 녀석들을 count합니다. 그리고 count한 값이 k(무료 개수)보다 크다면, 지출 상한을 높여주고 그렇지 않다면 지출 상한을 낮춰줍니다. 그리고 이렇게 찾은 지출 상한이 바로 답이 됩니다.</p>
<p><strong>주의</strong>: 못찾으면 -1을 출력해야함.</p>
<h1 id="코드-💻">코드 💻</h1>
<pre><code class="language-python">from heapq import heappop, heappush
import sys
input = sys.stdin.readline

n, p, k = map(int, input().split())
graph = [[] for _ in range(n+1)]

for _ in range(p):
    s, e, c = map(int, input().split())
    graph[s].append((e, c))
    graph[e].append((s, c))


def dijkstra(start):

    INF = 10001
    dist = [INF] * (n+1)
    dist[start] = 0

    pq = []
    heappush(pq, (0, start))

    while pq:
        cost, now = heappop(pq)

        if cost &gt; dist[now]: continue

        for nxt, c in graph[now]:
            if c &gt; mid:
                if dist[nxt] &gt; 1 + cost:
                    dist[nxt] = 1 + cost
                    heappush(pq, (1 + cost, nxt))
            else:
                if dist[nxt] &gt; cost:
                    dist[nxt] = cost
                    heappush(pq, (cost, nxt))

    return dist

start = 0
end = 100000001
answer = 100000001
while start &lt;= end:

    mid = (start + end) // 2
    result = dijkstra(1)

    if result[n] &gt; k:
        start = mid + 1
    elif result[n] &lt;= k:
        answer = mid
        end = mid - 1

if answer == 100000001:
    print(-1)
else:
    print(answer)</code></pre>
]]></description>
        </item>
    </channel>
</rss>