본문 바로가기

Beom Dev Log

  • 관리
  • 글쓰기
  • 로그인
  • 로그아웃
  • 홈
  • 태그
  • 방명록
Kotlin/Exercise

비밀 악수 - 10진수와 2진수

by 봄석 2019. 3. 20.

비밀 악수 - 10진수와 2진수

2진수에 관해서는 당신은 비밀스런 "악수"를 내놓기로 결정했습니다.

1 = wink
10 = double blink
100 = close your eyes
1000 = jump


10000 = Reverse the order of the operations in the secret handshake.

십진수가 주어지면 비밀 핸드 쉐이크를위한 적절한 이벤트 시퀀스로 변환합니다.

다음은 몇 가지 예입니다.

입력 3이 주어지면,이 함수는 3이 11 진법이므로 배열 [ "wink", "double blink"]을 반환합니다.

주어진 입력 값 19가 주어지면이 함수는 배열 [ "double blink", "wink"]을 반환 할 것입니다. 19가 이진수로 10011이기 때문입니다. 16 (이진수로 10000)을 추가하면 배열이 반전됩니다.



TestCode
 @Test
    fun testThatInput1YieldsAWink() {
        assertEquals(
            listOf(Signal.WINK),
            HandshakeCalculator.calculateHandshake(1))
    }
 
    @Ignore
    @Test
    fun testThatInput2YieldsADoubleBlink() {
        assertEquals(
            listOf(Signal.DOUBLE_BLINK),
            HandshakeCalculator.calculateHandshake(2))
    }
 
    @Ignore
    @Test
    fun testThatInput4YieldsACloseYourEyes() {
        assertEquals(
            listOf(Signal.CLOSE_YOUR_EYES),
            HandshakeCalculator.calculateHandshake(4))
    }
 
    @Ignore
    @Test
    fun testThatInput8YieldsAJump() {
        assertEquals(
            listOf(Signal.JUMP),
            HandshakeCalculator.calculateHandshake(8))
    }
 
    @Ignore
    @Test
    fun testAnInputThatYieldsTwoActions() {
        assertEquals(
            listOf(Signal.WINK, Signal.DOUBLE_BLINK),
            HandshakeCalculator.calculateHandshake(3))
    }
 
    @Ignore
    @Test
    fun testAnInputThatYieldsTwoReversedActions() {
        assertEquals(
            listOf(Signal.DOUBLE_BLINK, Signal.WINK),
            HandshakeCalculator.calculateHandshake(19))
    }
 
    @Ignore
    @Test
    fun testReversingASingleActionYieldsTheSameAction() {
        assertEquals(
            listOf(Signal.JUMP),
            HandshakeCalculator.calculateHandshake(24))
    }
 
    @Ignore
    @Test
    fun testReversingNoActionsYieldsNoActions() {
        assertEquals(
            emptyList(),
            HandshakeCalculator.calculateHandshake(16))
    }
 
    @Ignore
    @Test
    fun testInputThatYieldsAllActions() {
        assertEquals(
            listOf(Signal.WINK, Signal.DOUBLE_BLINK, Signal.CLOSE_YOUR_EYES, Signal.JUMP),
            HandshakeCalculator.calculateHandshake(15))
    }
 
    @Ignore
    @Test
    fun testInputThatYieldsAllActionsReversed() {
        assertEquals(
            listOf(Signal.JUMP, Signal.CLOSE_YOUR_EYES, Signal.DOUBLE_BLINK, Signal.WINK),
            HandshakeCalculator.calculateHandshake(31))
    }
 
    @Ignore
    @Test
    fun testThatInput0YieldsNoActions() {
        assertEquals(
            emptyList(),
            HandshakeCalculator.calculateHandshake(0))
    }
 
    @Ignore
    @Test
    fun testThatInputWithLower5BitsNotSetYieldsNoActions() {
        assertEquals(
            emptyList(),
            HandshakeCalculator.calculateHandshake(32))
    }




해결안1) 재귀 함수

fun calculateHandshake(n: Int): List<Signal> =
             if (n >= 16)
                 calculateHandshake(n - 16).reversed()
             else if (n >= 8)
                 calculateHandshake(n - 8) + listOf(Signal.JUMP)
             else if (n >= 4)
                 calculateHandshake(n - 4) + listOf(Signal.CLOSE_YOUR_EYES)
             else if (n >= 2)
                 calculateHandshake(n - 2) + listOf(Signal.DOUBLE_BLINK)
             else if (n >= 1)
                 listOf(Signal.WINK)
             else

                 listOf()

1) 먼저 들어온 인수 n에 대하여  16이상인지 확인합니다 .

2) 만약 16이상이라면 들어온 인수에대하여 -16을 하고 다시 자기자신을 호출합니다. 그리고 리턴값(리스트)을 거꾸로 뒤집습니다 .

3) 그리고 다시 호출된 재귀함수 자기자신에서 그다음 else if문으로 들어가서 조건에 맞다면 리스트에 값을 추가하고 자기자신을 다시 호출하는 식으로 반복합니다 .

4) 최종 결과값은 리스트로 리턴합니다 .


해결안 2) 중위함수 ,비트 시프트(shr) ,비트연산 and , apply

 private infix fun Int.hasBitSet(bit: Int) = ((this shr bit) and 0x1) == 1
 
        fun calculateHandshake(number: Int): List<Signal> {
            return mutableListOf<Signal>().apply {
                if (number hasBitSet 0) add(Signal.WINK)
                if (number hasBitSet 1) add(Signal.DOUBLE_BLINK)
                if (number hasBitSet 2) add(Signal.CLOSE_YOUR_EYES)
                if (number hasBitSet 3) add(Signal.JUMP)
                if (number hasBitSet 4) reverse()
            }

        }

1) 먼저 중위함수를 선언합니다 . 중위함수는 infix 키워드를 fun 앞에 붙여 만들수 있습니다 .

number hasBitSet 0 처럼 확장함수를 중간에 사용할 수 있습니다.

2) 그리고 shr 연산자를 이용하여 중위연산자 왼쪽의 int 값을 오른쪽으로 매개변수 bit 만큼 비트연산합니다 (1/2) 계산

3) 위의 시프트 한 값을 다시 1과 and 연산하여 그 값을 정수 1과 비교합니다 .

4) calculatorHandshake 함수로 돌아가서 mutableListOf 함수로 리스트를 생성하고 , apply() 함수를 통하여 

자기자신을 인자로 받고 , 자기자신을 리턴하게 됩니다 .

5) apply 함수 안에서 위에서 선언했던 중위 확장함수를 이용하여  그값이 true 라면 mutableList에 값을 추가합니다 .

6) hasBitSet은 총 5번 호출되며, 마지막 number hasBitSet 4 가 트루라면 리스트를 거꾸로 뒤집습니다 .




Infix

infix 키워드를 사용해서 중위(Infix) 표현으로 사용할 수 도 있습니다.

fun Int.multiply(x: Int): Int {
    return this * x
}

위의 코드는 Int를 확장한 확장 함수이다. 간단하게 생각하면 Int 클래스의 함수를 더 추가 한다고 생각하면 됩니다.

val multiply = 3.multiply(10)

그럼 위와 같이 확장함수를 이용해서 간단하게 만든 multiply 를 사용할 수 있다. 중위 표현으로 변경하면 다음과 같습니다.

infix fun Int.multiply(x: Int): Int {
    return this * x
}

위와 같이 infix 키워드를 사용하면 중위 표현을 사용할 수 있습니다 .

val multiply = 3 multiply 10



해결안 3) 2진 표현법 (0b) , map, let, 확장함수 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private val handshakeCodes = mapOf(
              0b0001 to Signal.WINK,
              0b0010 to Signal.DOUBLE_BLINK,
              0b0100 to Signal.CLOSE_YOUR_EYES,
              0b1000 to Signal.JUMP
          )
          private val REVERSE = 0b10000
 
          fun calculateHandshake(flags: Int) = handshakeCodes
              .map { if (flags.hasFlag(it.key)) it.value else null }
              .filterNotNull()
              .let { if (flags.hasFlag(REVERSE)) it.asReversed() else it }
 
          private fun Int.hasFlag(flag: Int) = // this and flag != 0
              and(flag) != 0
Colored by Color Scripter
cs

1)변수 handshakeCodes에 맵을 생성합니다. key는 2진수값, value는 enum값을 넣어줍니다 .

2) calculateHandshake 함수에서 위의 변수를 map{} 으로 변환합니다 .

3) map{} 안에서는 handshakeCodes 변수의 맵의 각 인덱스 값을 가져와 if문으로 확인합니다.

4) if 문 안에서는 확장함수 hasFlag 를 호출하며 ,hasFlag 함수는 확장함수로 , Int   값과 매개변수로 받은 값을 and연산하고, 그값이 0과 다르다면 true를 리턴합니다 .

5) 만약 calculateHandshake 함수에 4가 들어왔다면 맵의 키 0b0100과 같을때만 맵의 value값을 가져와 리스트에 추가합니다 . map{}의 반환형은 List 입니다 .


map

지정된 변환 함수를 원의 char 순서의 각 문자에 적용한 결과를 포함한리스트를 돌려줍니다 .

fun <R> CharSequence.map(transform: (Char) -> R): List<R>

6) list에서 null이 아닌값만 필터하고 리턴값은 List입니다 .


filterNotNull 

fun <T : Any> Array<out T?>.filterNotNull(): List<T>

fun <T : Any> Iterable<T?>.filterNotNull(): List<T>


7) let함수를 통하여 자기자신을 인자로 넘겨서 if문을 실행합니다 .

8) if 문에서는 calculateHandshake로 넘겨받은 값이 16이라면 리스트를 뒤집습니다 .



해결안 4) enum.values(), ordinal, 

  private const val REVERSE_BITS_POSITION = 4
 
          fun calculateHandshake(decimalNumber: Int): List<Signal> {
              val result = Signal.values().filter {
                  isBitSet(it.ordinal, decimalNumber) }
 
              return if (isBitSet(REVERSE_BITS_POSITION, decimalNumber)) result.reversed() else result
          }
 
          private fun isBitSet(position: Int, number: Int): Boolean {
              return number shr position and 1 == 1

          }

1) calcultateHandshake 함수에서 EnumClass Signal의 values함수를 호출합니다 .

EnumClass.valueOf(value: String): EnumClass

EnumClass.values(): Array<EnumClass>

을 통하여 Enum클래스의 상수에 접근할 수있습니다 .

2) EnumClass.values()로 얻은 어레이리스트를 filter 하는데 

3) isBitSet 이라는 확장함수가 true인것만 필터합니다 .

4) isBitSet 함수의 매개변수로 첫번째로 ordinal을 받는데 , ordinal은 Enum의 선언 위치입니다 .(Int)


ordinal

val ordinal: Int

이 열거 정수의 서수 (열거 형 선언의 위치, 초기 상수에 0의 서수가 할당 됨)를 리턴합니다.
두번째 매개변수로는 calculateHandshake의 매개변수로 받은 Int 값을 전달합니다 .

5) isBitset 함수 내에서는  calculateHandshake의 매개변수를 Enum의 ordinal만큼 시프트 라이트 하여 , 

1과 비교하여 같다면 true를 리턴합니다 

6)결과적으로 result에는 Enum에서 필터링된 값들의 리스트가 남게되고 , 마지막으로 Reverse 인지를 체크하여 리스트를 리턴합니다 .


'Kotlin > Exercise' 카테고리의 다른 글

완벽한 숫자 - 자연수에대한  (2) 2019.03.27
정사각형의 차이  (4) 2019.03.17
스크래블 스코어 - 주어진 단어에대하여 점수 계산하기  (4) 2019.03.17
어구를 약어로 변경하기  (2) 2019.03.14
스트링 뒤집기  (2) 2019.03.13

관련글

  • 완벽한 숫자 - 자연수에대한
  • 정사각형의 차이
  • 스크래블 스코어 - 주어진 단어에대하여 점수 계산하기
  • 어구를 약어로 변경하기

댓글

  • 분류 전체보기
    • Daily
    • Android
      • Android관련 이것 저것..
      • Test
      • Architecture
      • JetPack
      • DI
    • Flutter
    • Rx
    • JAVA
    • Kotlin
      • Exercise
    • DesignPattern
    • Git
    • CI , CD
      • Docker
      • AWS,JENKINS
      • Sonarqube
    • FireBase
    • Review
    • ETC

최근글

인기글

태그

도커 Flutter scheduler Observable AWS map RxJava jenkins 점화식 kotlin List dart language Stack Git zip Design Pattern BOJ DART rxandroid CI subject When reduce Java Firebase Filter Docker Jetpack joinToString dp

전체 방문자

Today :

Yesterday :


TOP

티스토리툴바