카테고리 글 목록: 자바스크립트 프로그래밍
재귀함수, 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()