Learn Programming with CoffeeScript

코딩 따라가기 (1)

 

코딩 따라가기

이미 만들어 둔 로또 등수 결정하기를 수정하며 설명한다. 그러니 앞내용을 안보았으면 보고 온다.

이 코드 개선점은?

여러가지 개선할 점이 있겠지만, 2가지를 생각해보자.

  1. 로또 번호는 하나의 덩어리 정보인데, 이것을 값으로만 다루고 있다.
  2. GetCountOfMatchedNumber의 내용이 심각할 정도로 반복된다.

로또 번호를 어떻게 표현할것인가?

정보는 배열(Array)와 객체(Object)를 이용해서 표현하면 된다.

로또 번호를 7개의 숫자로 이루어져있고, 이 숫자는 딱히 구분되지 않는다면, 배열이라면 충분할것이다

WinNumberOf598 = [4,12,24,33,38,45,22 ]
LottoNumber = [ 4,12,24,11,38,36,12]

이런 형태면 되겠다.

그런데!

아래 그림을 잘 보라. 정말 숫자 7개가 서로 구분이 없는가?

정확히는 보너스번호라는 구분이 있다.

그렇다면, 마킹한 숫자와 보너스번호를 구분해야할것이다. 이를 표현에 반영해 보자.

WinNumberOf598 = 
  markNumbers: [4,12,24,33,38,45]
  bonus: 22
LottoNumber =
  markNumbers : [ 4,12,24,11,38,36]
  bonus : 12

이렇게 표현하니 7개의 숫자가 가지는 의미를 더욱 정확히 표현하였다.

이제 이를 이용해서 기존의 작성한 코드를 고쳐보자.

GetMyRank의 파라메터 형태

우선 GetMyRank가 단순 숫자가 7개 아니라 로또 번호를 입력으로 받도록 해야겠다.

그렇다면 함수 정의 및 호출은 아래와 같게 고칠수 있겠다.

GetMyRank = (lottoNumber)-> 
  cnt = GetCountOfMatchedNumber(N1,N2,N3,N4,N5,N6) 
  if cnt is 6
    return 1
  else if cnt is 5
    if NBonus is WinNumberBonus
      return 2
    return 3
  else if cnt is 4
    return 4
  else if cnt is 3
    return 5
  else 
    return null

LottoNumber =
  markNumbers : [ 4,12,24,11,38,36]
  bonus : 12

GetMyRank(LottoNumber)

이부분만 바꾸고 실행하면 에러가 날것이다.

N1 이 정의가 되지 않았다고 한다.

GetCountOfMatchedNumber함수를 사용할 때 없애버린 N1 변수를 주었으니당연한 결과이겠다.

GetMyRank의 구현

그럼 구현도 맞춰서 바꿔보자.

GetMyRank = (lottoNumber)-> 

  N1 = lottoNumber.markNumbers[0]
  N2 = lottoNumber.markNumbers[1]
  N3 = lottoNumber.markNumbers[2]
  N4 = lottoNumber.markNumbers[3]
  N5 = lottoNumber.markNumbers[4]
  N6 = lottoNumber.markNumbers[5]
  cnt = GetCountOfMatchedNumber(N1,N2,N3,N4,N5,N6)

  if cnt is 6
    return 1
  else if cnt is 5
    if NBonus is WinNumberBonus
      return 2
    return 3
  else if cnt is 4
    return 4
  else if cnt is 3
    return 5
  else 
    return null


LottoNumber =
  markNumbers : [ 4,12,24,11,38,36]
  bonus : 12

rank = GetMyRank(LottoNumber)
console.log rank

이렇게만 바꿔도 잘 실행된다. 하지만 좋아보이지는 않는다. GetCountOfMatchedNumber의 파라메터 형태 역시 lottoNumber 정보를 사용하게끔 바꾸어보자.

일단 호출하는쪽에서 파라메터를 바꾸자.

GetMyRank = (lottoNumber)-> 

  cnt = GetCountOfMatchedNumber lottoNumber

그리고 GetCountOfMatchedNumber의 생김새도 바꾼다. 이번에도 N1이 없어지면서 에러날게 분명하니 애초에 바꿔버리자.

GetCountOfMatchedNumber = (lottoNumber)->

  N1 = lottoNumber.markNumbers[0]
  N2 = lottoNumber.markNumbers[1]
  N3 = lottoNumber.markNumbers[2]
  N4 = lottoNumber.markNumbers[3]
  N5 = lottoNumber.markNumbers[4]
  N6 = lottoNumber.markNumbers[5]

실행해보면,

잘 나온다.

그런데 확인해봐야 할것이 있다.

로또 2등이다. 2등은 이전코드에서 NBonus를 사용했는데, 이번에 수정하면서 없애버렸다.

2등 번호로 입력을 바꿔서 실행보자. 오류가 날것이다.

이부분을 마저 수정하면.

GetMyRank = (lottoNumber)-> 

  cnt = GetCountOfMatchedNumber lottoNumber

  if cnt is 6
    return 1
  else if cnt is 5
    if lottoNumber.bonus is WinNumberBonus #  보너스 번호에 접근방법이 바뀌었다.
      return 2
    return 3
  else if cnt is 4
    return 4
  else if cnt is 3
    return 5
  else 
    return null

이렇게 될것이다.


이런 형태로 함수가 받는 입력의 의미를 구체화할수 있으며,

사람이 읽기에도 좀 더 의미를 생각하게 바꿀수 있다.

GetCountOfMatchedNumber의 구현

이번엔 GetCountOfMatchedNumber의 내용을 바꾸어보자.

이전 수업에서 정보를 취급하는 법으로 for를 배웠다.

이를 사용하여 변경할 것이다.

코드의 조각 조각이 어떻게 변하는지 한번 보자.

  if WinNumber1 is N1
    cnt = cnt + 1
  if WinNumber1 is N2
    cnt = cnt + 1
  if WinNumber1 is N3
    cnt = cnt + 1
  if WinNumber1 is N4
    cnt = cnt + 1
  if WinNumber1 is N5
    cnt = cnt + 1
  if WinNumber1 is N6
    cnt = cnt + 1

는 아래와 같다.

  if WinNumber1 is lottoNumber.markNumbers[0]
    cnt = cnt + 1
  if WinNumber1 is lottoNumber.markNumbers[1]
    cnt = cnt + 1
  if WinNumber1 is lottoNumber.markNumbers[2]
    cnt = cnt + 1
  if WinNumber1 is lottoNumber.markNumbers[3]
    cnt = cnt + 1
  if WinNumber1 is lottoNumber.markNumbers[4]
    cnt = cnt + 1
  if WinNumber1 is lottoNumber.markNumbers[5]
    cnt = cnt + 1

이처럼 배열 lottoNumber.markNumbers의 요소값 별로 반복호출이되면 for를 사용해 반복 호출을 지시하면된다.

for 명령을 적용하면 아래의 코드는...

  if WinNumber1 is N1
    cnt = cnt + 1
  if WinNumber1 is N2
    cnt = cnt + 1
  if WinNumber1 is N3
    cnt = cnt + 1
  if WinNumber1 is N4
    cnt = cnt + 1
  if WinNumber1 is N5
    cnt = cnt + 1
  if WinNumber1 is N6
    cnt = cnt + 1

이렇게 바뀐다...

  for num in lottoNumber.markNumbers
    if WinNumber1 is num
      cnt = cnt + 1

양도 확 줄었고 보기에도 좋다.

들여쓰기에 주의하라. 초보가 소스를 고칠때 많이 하는 실수 중 하나가 들여쓰기를 다시 정리하지 않아서 명령이 엉뚱하게 수행되게 하는 것이다. 그러니 소스를 고치면, 들여쓰기도 재점검하라

다른 WinNubmer 에도 적용하면 코드는 이렇게 줄어든다.

GetCountOfMatchedNumber = (lottoNumber)-> 
  cnt = 0
  for num in lottoNumber.markNumbers
    if WinNumber1 is num
      cnt = cnt + 1

  for num in lottoNumber.markNumbers
    if WinNumber2 is num
      cnt = cnt + 1

  for num in lottoNumber.markNumbers
    if WinNumber3 is num
      cnt = cnt + 1

  for num in lottoNumber.markNumbers
    if WinNumber4 is num
      cnt = cnt + 1

  for num in lottoNumber.markNumbers
    if WinNumber5 is num
      cnt = cnt + 1

  for num in lottoNumber.markNumbers
    if WinNumber6 is num
      cnt = cnt + 1

  return cnt

그런데 이렇게 보니, WinNumber1~6으로 바뀔뿐 실행하는 내용이 같다.

그러면 이부분도 for를 이용할수 있을것이다.

  for num in lottoNumber.markNumbers
    if WinNumber1 is num
      cnt = cnt + 1 

위 코드 만큼의 명령 덩어리를 WinNumber1~6에 대하여 1번씩 호출해주면 되니, WinNumber1~6를 배열에 넣고, 임시변수를 바꿔가며 위 명령을 실행하면 되겠다.

WinNumber의 배열을 아래처럼 만들고..

  WinNumbers = [ 
    WinNumber1
    WinNumber2
    WinNumber3
    WinNumber4
    WinNumber5
    WinNumber6
  ]

for를 이용해 반복한 명령들을 수행한다. 아래처럼.

GetCountOfMatchedNumber = (lottoNumber)->
  cnt = 0
  WinNumbers = [ 
    WinNumber1
    WinNumber2
    WinNumber3
    WinNumber4
    WinNumber5
    WinNumber6
  ]

  for winNum in WinNumbers
    for num in lottoNumber.markNumbers
      if winNum is num
        cnt = cnt + 1

  return cnt

리뷰

120줄에 이르던 코드를 50여줄로 만들수 있었고,

보다 명확한 의미로 코드를 쓸수 있었다. (N7보다 lottoNumber.bonus가 더 의미가 명확하지 않은가?)

데이터를 좀더 큰 덩어리인 배열과 객체로 다루는 것은 숙달되어야 하는 부분이다. 배열과 객체를 이용해야, 코드의 의미가 명확히지고, 간결해지며, 작성하기 쉽고, 고치기 쉬워진다.

그리고 정보를 어떻게 표현하는 것이 좋은것인지는 연습이 많이 필요한 부분이다. 사실 무엇이 좋은지 가치판단하기 쉽지 않은 부분이라 노력이 필요하다.

그러나 같은 값을 다루어도 표현이 보다 구조적이면, 명령도 간결해지는등 많은 장점이 있으니 꼭 거쳐야한다.

댓글

댓글 본문
버전 관리
Chunsik Hwang
현재 버전
선택 버전
graphittie 자세히 보기