카테고리 글 목록: 자바스크립트 프로그래밍

재귀함수, arguments.callee

함수안에서 자신을 다시 부를 수 있다.
흔히 재귀함수라고 하는것인데.

factorial(팩토리얼) 알고리즘을 예로 들어 설명한다.

‘팩토리얼 5’의 결과값은 120이다.

5 x 4 x 3 x 2 x 1 = 120

수식은 자신’5’부터 ‘1’까지 1씩 줄여가며 곱하는 것이다

이제 이것을 함수 이름으로 선언하면

function factorial (x) {
 if (x <= 1) 
 return 1;

 return x * factorial(x - 1);
}

함수 리터럴로 선언하면

var factorial = function(x) {
 if (x <= 1) 
 return 1;

 return x * factorial(x - 1);
}

그런데 만약 함수리터럴에서 다른이름으로 할당한다면 어떻게 될까 ?

factorial(5); // 120  기존함수
var fact = factorial; // 새 변수에 기존함수 할당
factorial = null; // 기존 함수 제거

fact(5); // error!!

fact 함수안에서 자신이 아닌 factorial을 부르고 있기때문에 자신을 찾을 수가 없다.

함수의 이름을 사용하지 말고 arguments.callee 를 사용해 자신을 불러보자.

var factorial = function(x) {
 if (x <= 1) 
 return 1;

 return x * arguments.callee(x - 1); // 자기자신을 부름
}

이렇게 되면 같은 과정을 거쳐 할당을 해도 작동을 한다.

factorial(5); // 120  기존함수
var fact = factorial; // 새 변수에 기존함수 할당
factorial = null; // 기존 함수 제거

fact(5); // 120 정상작동

for…in 문 사용시 주의할 점

getElementsByTagName 으로 특정Tag를 선택한후 for/in 문으로 인덱스를 뿌렸을때 이상한 것을 발견했다.

<h1>보라돌이</h1>
<h1>뚜비</h1>
<h1>나나</h1>
<h1>뽀</h1>
var tagH1 = document.getElementsByTagName("h1");

for(var i in tagH1){
  console.log(i);  // 0,1,2,3,length,item,namedItem
}

0,1,2,3 뿐만 아니라 length,item,namedItem 이란 요소가 따라왔다.
다시

document.getElementsByTagName("h1")[0]  // <h1>보라돌이</h1>
document.getElementsByTagName("h1")[1]  // <h1>뚜비</h1>
document.getElementsByTagName("h1")[2]  // <h1>나나</h1>
document.getElementsByTagName("h1")[3]  // <h1>뽀</h1>
document.getElementsByTagName("h1")[4]  // undefined

배열로 접근한다고 잡히지도 않는다.

getElementsByTagName 는 배열이 아니라 nodelist이다.

더군다나 for in 문은 배열의 인덱스를 출력하는것이라기보다 오브젝트의 property를 뱉어낸다.
배열도 오브젝트이다. 인덱스를 뿌려줄수있으나 nodelist라던가 특정 object의 경우 프로퍼티까지 다 나온다.

반대로 배열에서

var a = [ , , 3];

의 경우  일반 for문을 돌리면

for(var i=0; i < a.length ; i++) 
 console.log(a[i]); // undefined, undefined, 3

이렇게 나오지만  for/in문을 쓰면

for(var i in a)
console.log(i); // 3

이렇게 존재하는 요소만 출력한다.

javascript 로또 자동번호 생성

우리나라 로또추첨방식은 6/45 즉, 45개의 공중에 6개의 공을 선택한다.
45개의 숫자중 6개의 숫자를 무작위로 선택 후 작은번호부터 재배열 하는것이다.

기존의  알고리즘이 난수를 발생하여 45개중 공을 선택하여 기존의 공과 중복이 되는지를 검출했다면
좀더 효율적인 방법이 없을까 하여. 한번 뽑아낸 공은 제외시키고 그 다음공은 나머지 공에서 추출하는 방법을 고민해보았다.
(이 방법이 현실과 닮아 있긴하다)

  • 일단 반복문을 통해 push()로 1 ~ 45번까지 aryLotto 배열에 담는다.
    그리고 바로  여섯 번에 걸쳐 공을 꺼내게 되는데
  • Math.random() 난수발생 후  Math.floor()를 통해 0~44 까지의 임의 숫자를 생성한다.
    보통 1~45의 숫자를 선택하기 위해 난수발생후 +1을 해주지만 이번방식은 배열의 위치만을 사용할것이므로 그대로 쓴다.
  • 만약 첫번째 난수로 33가 나왔다면 이는 ’34’이란 숫자가 선택된것이다. (idx 변수에 담았다)
    이 34이란 숫자는 나의 로또 번호중 첫번째가 되었다. 이를 배열 myLotto에 push()해서담는다.
  • 담고나서 기존 45개 배열aryLotto에서 선택된 숫자 ’34’ (배열index 로는 33)를 splice()를 통해 삭제한다. splice(idx,1) 에서 첫번째 인자는 배열 index이고 두번째 인자는 갯수이다.(한개의 번호만 제외하는것이므로 1을 사용했다.)
var myLotto = [];
var aryLotto = [];
var idx = 0;

for (var i = 1; i < 46; ++i) aryLotto.push(i)  

while (myLotto.length < 6){
    idx = Math.floor(Math.random()*aryLotto.length);
    myLotto.push(aryLotto[idx]);
    aryLotto.splice(idx,1);
}
  
myLotto.sort(sortNumber);
  
function sortNumber(a, b) {
  return a - b;
}
  • 다시 정리하면 45개의 숫자가 담긴 배열에서 33번째숫자(34)를 선택해서 새배열에 담고
    그 33번째 숫자(34)를 삭제한것이다.
  • 이 과정이 5번반복되면 되는데 중요한것은 빠진 숫자만큼 전체갯수가 줄었기때문에 난수발생시 그만큼 숫자를 줄여야 한다. (첨에 45개중에 골랐다면 그담은 44개중에 골라야하므로)  이는 aryLotto의 배열의 길이(aryLotto.length)와 같으므로 난수발생시 이 길이를 곱해주면된다.
  • 5개의 숫자가 담겼다. 하지만 무작위로 선택된 5개의 숫자를  작은 수 부터 정렬하면 좋겠다. javascript에서 sort()라는 정렬함수를 제공하지만 이는 수를 크기별로 정렬하지 못하고 알파벳순으로 정렬한다.(예를들어 5, 11,7,1,3,10,8 를 sort로 정렬하면 1,10,11,3,5,8 로 정렬된다  우리가 기대한 1,3,5,7,8,10,11이 아니다) 그러므로 펑션을 하나 써서 sort를 해주어야한다.
    function sortNumber(a, b) {
      return a - b;
    }
    aryMyLott.sort(sortNumber);
  • 이제 정렬이 제대로 된다.

이로서 javascript를 이용한  중복검사를 하지 않는 로또자동생성 프로그램을 만들어 보았다.이 프로그램에 사용된 js함수를 정리하면 다음과 같다.

  • push()
  • Math.random()
  • Math.floor()
  • splice()
  • sort()