프론트엔드/Javascript

반복문 (loop)

syleemomo 2024. 2. 17. 13:02
728x90

* while 반복문

while (condition) {
  // 코드
  // '반복문 본문(body)'이라 불림
}

조건(condition) 이 truthy 이면 반복문 본문의 코드가 실행된다. 반복문 본문이 한번 실행되는 것을 반복(iternation)이라고 한다.

let i = 0;
while (i < 3) { // 0, 1, 2가 출력됩니다.
  alert( i );
  i++;
}

해당 반복문은 i 값을 1씩 증가시켜가면서 i < 3 의 조건을 만족하는 동안 i 값을 경고창에 출력한다. 해당 반복문은 세번의 이터레이션을 수행한다. 

let i = 3;
while (i) { // i가 0이 되면 조건이 falsy가 되므로 반복문이 멈춥니다.
  alert( i );
  i--;
}

반복문 조건에는 모든 종류의 표현식이나 변수가 들어갈 수 있다. 조건은 while 에 의하여 평가되고, 평가 결과는 불린값이다. 해당 반복문은 맨 처음 i (3) 이 truthy 이므로 3이 출력되고, 1만큼 감소하여 2가 된다. 2도 truthy 이므로 출력되고, 다시 1만큼 감소하여 1이 된다. 1도 truthy 이므로 출력되고, 1만큼 감소하여 0이 된다. 0은 falsy 이므로 조건을 만족하지 못하므로 반복은 종료된다. 

let i = 3;
while (i) alert(i--);

본문이 한줄이면 중괄호는 생략이 가능하다. 

let i = 0;
while (i < 3) { // 0, 1, 2가 출력됩니다.
  alert( i );
}

만약 i++ 코드가 없다면 i 는 항상 0이고 조건은 항상 truthy 이므로 반복문은 이론적으로 영원히 실행된다. 하지만 브라우저나 서버사이드 자바스크립트는 이러한 무한반복을 종료할 수 있는 기능이 있으므로 실질적으로는 영원히 반복되지는 않는다. 

let value = 3
while(value <= 3){
  let value = 5
}
console.log('출력되지 않음')

해당 반복문은 무한루프에 빠져서 콘솔창에 아무것도 출력되지 않는다. 이유는 let 이 블록스코프이기 때문이다. 블록스코프로 선언된 value 변수값은 초기에 3이다. 그러므로 while 문의 조건을 만족하여 반복문의 코드블럭이 실행된다. 이때 반복문 안에서 let 키워드를 사용하여 value 값을 5로 변경한다. 하지만 let은 블록스코프이므로 내부에서 선언한 5는 다음 이터레이션으로 가기전에 메모리에서 사라진다. 결국 반복조건을 검사할때는 항상 반복문 밖에서 선언한 value 값 3으로 검사가 이루어지므로 무한루프에 빠진다.

let value = 3
while(value <= 3){
  value = 5
}
console.log('이번에는 출력이 됨')

이러한 문제를 해결하기 위해서 반복문 내부의 코드블럭에서는 반복문 바깥에서 선언한 value 값을 변경해주면 된다. 그럼 두번째 이터레이션에서는 조건을 만족하지 못하므로 반복문이 종료되고 콘솔창에 메세지가 출력이 된다. 

let a = 1

while(a < 100){
  if(a % 5 === 0){
    console.log(a)
  }
  a++
}

해당 반복문은 100보다 작은 자연수 중에서 5의 배수를 출력한다. 

 

let a = 1

while(a < 100 && a % 5 === 0){
  console.log(a)
  a++
}

a 가 100보다 작은 자연수이고, a 를 5로 나눈 나머지가 0이므로 while 문과 if 조건에 따로 작성하지 않고 깔끔하게 위와 같이 작성하면 좋지 않을까? 

하지만 결과적으로 콘솔창에 아무것도 출력이 되지 않는다. 이유는 처음 조건을 검사할때 a 는 100보다 작지만, a 가 1이므로 5로 나눈 나머지가 0이 아니다. 즉, 5의 배수가 아니면 반복문은 실행조차 되지 않는다. 조건을 만족하지 못하므로 반복문은 종료된다. 결론적으로, while 문 안에 들어가는 조건에는 반복을 수행할 범위에 대한 조건만 포함되어야 한다. 

let a = 1
let enable = true 

while(a < 100 && enable){
  if(a % 5 === 0){
    console.log(a)
  }
  a++
}
let a = 1
let enable = false 

while(a < 100 && enable){
  if(a % 5 === 0){
    console.log(a)
  }
  a++
}

하지만 여전히 while 문에는 여러개의 조건이 첨가될 수 있다. 해당 반복문은 반복문을 실행할지 말지에 대한 활성화 조건을 추가하였다. enable 이 true 이면 반복문이 활성화되어 실행되고, false 이면 반복문은 실행되지 않는다. 

let a = 1

while(true){
  if(a % 5 === 0){
    console.log(a)
  }
  a++
}

while 문의 조건이 true 이면 반복문은 항상 실행되므로 무한루프에 빠진다. 

let a = 1

while(a > 0){
  if(a % 5 === 0){
    console.log(a)
  }
  a++
}

while 문의 조건이 true 가 아니더라도 해당 반복문은 무한루프에 빠진다. a 는 1부터 시작해서 계속 1씩 증가하며, while 문의 조건은 0보다 큰 경우로 설정하였기 때문이다. 이렇게 되면 a 값은 항상 0보다 크므로 반복문은 종료되지 않는다. 

let a = 1

while(a > 0 && a < 100){
  if(a % 5 === 0){
    console.log(a)
  }
  a++
}

현재 상황에서는 반복문이 실행되는 범위를 100보다 작은 경우로 범위를 좁혀주면 무한루프에 더이상 빠지지 않는다. 

 

* do while 반복문

do {
  // 반복문 본문
} while (condition);

do while 반복문은 while 문과 달리 반복문의 코드블럭이 최소 한번은 실행된다. 이후 조건(condition)을 검사하여 truthy 인 동안 반복문의 코드블럭은 계속 실행된다. 

let i = 0;
do {
  alert( i );
  i++;
} while (i < 3);

해당 반복문은 i 값이 3보다 크거나 같더라도 적어도 한번은 실행된다. 

let a = 1

do{
  if(a % 5 === 0){
    console.log(a)
  }
  a++
}while(a < 100)

100보다 작은 자연수 중에서 5의 배수를 출력하는 프로그램을 do ~ while 반복문으로 바꾸면 위와 같다. 

 

* for 반복문

for (begin; condition; step) {
  // ... 반복문 본문 ...
}

for 반복문은 아래와 같은 순서로 진행된다. 

1. begin 은 맨 처음에 한번만 실행된다. 
2. 조건(condition)을 검사한다.
3. 조건이 참이면 반복문 본문이 실행된다. 
4. step 이 실행된다. 
5. 2번부터 다시 수행한다. 
6. 2번에서 조건이 거짓이면 반복은 종료된다.

해당 반복문을 실행하면 0, 1, 2가 출력된다. 

for (let i = 0; i < 3; i++) { // 0, 1, 2가 출력됩니다.
  alert(i);
}

만약 해당 반복문을 풀어서 조건문으로 작성하면 아래와 같다.

// for (let i = 0; i < 3; i++) alert(i)

// begin을 실행함
let i = 0
// condition이 truthy이면 → body를 실행한 후, step을 실행함
if (i < 3) { alert(i); i++ }
// condition이 truthy이면 → body를 실행한 후, step을 실행함
if (i < 3) { alert(i); i++ }
// condition이 truthy이면 → body를 실행한 후, step을 실행함
if (i < 3) { alert(i); i++ }
// i == 3이므로 반복문 종료

카운터 변수 i 를 반복문 안에서 선언하는 것을 인라인 변수 선언이라고 한다. let 으로 선언한 i 변수는 블록 스코프이므로 반복문을 벗어나면 메모리에서 사라진다. 그러므로 반복문 밖에서 사용할 수 없다. 

for (let i = 0; i < 3; i++) {
  alert(i); // 0, 1, 2
}
alert(i); // Error: i is not defined

만약 반복문을 벗어난 이후에도 i 변수를 사용하려면 반복문 밖에서 (전역공간)  변수선언을 하면 된다. 

let i;

for (i = 0; i < 3; i++) { // 기존에 정의된 변수 사용
  alert(i); // 0, 1, 2
}

alert(i); // 3, 반복문 밖에서 선언한 변수이므로 사용할 수 있음

아래와 같이 for 문의 구성요소를 생략할 수도 있다. 아래는 begin 이 생략된 형태이다. 

let i = 0; // i를 선언하고 값도 할당하였습니다.

for (; i < 3; i++) { // 'begin'이 필요하지 않기 때문에 생략하였습니다.
  alert( i ); // 0, 1, 2
}

step 도 생략할 수 있다. 이렇게 하면 while(i < 3) 과 동일하다.

let i = 0;

for (; i < 3;) {
  alert( i++ );
}

모든 구성요소를 생략하면 무한반복문이 된다. 

for (;;) {
  // 끊임 없이 본문이 실행됩니다.
}

 

step은 1 이외에 다른 값이 가능하다. 

for(let i = 3; i<100; i+=3){
  console.log(i)
}

해당 코드는 step 이 3이고, 100보다 작은 자연수에서 3의 배수를 출력하는 코드이다. 

for(let i = 0; i<1; i+=0.1){
  console.log(i)
}

step 은 소수점도 가능하다. 해당 코드는 아래와 같은 값들을 출력한다.

 

for(let i = 0; i<1; i+=0.1){
  console.log(i.toFixed(1))
}

toFixed 함수는 소수점의 몇째자리까지 선택할지 결정한다. 결과값은 문자열이며, 소수점 이하가 길면 반올림한다. 

 

* 반복문 빠져나오기 (break)

break 문을 사용하면 언제든 원하는 시점에 반복문을 빠져나올수 있다. 특히나 무한루프에 빠질 상황에서는 말이다. 

let sum = 0;

while (true) {

  let value = +prompt("숫자를 입력하세요.", '');

  if (!value) break; // (*)

  sum += value;

}
alert( '합계: ' + sum );

아래와 반복문은 조건이 true 이므로 이론상 무한반복된다. 반복하면서 사용자로부터 숫자를 입력받는다. 

1. 사용자가 아무것도 입력하지 않는 경우
2. 사용자가 숫자 이외의 문자열, 특수문자 등을 입력한 경우
3. 사용자가 취소버튼을 누른 경우
4. 사용자가 0을 입력한 경우

해당 반복문은 위의 4가지 경우 중 하나를 만족하면 break 문이 실행되면서 반복문이 종료된다. 위의 4가지 경우가 아니라면 sum 변수에 사용자가 입력한 숫자를 계속 더한다. 반복문이 종료되면 합계(sum)를 출력한다. 

whie(true){
	if(조건) break
}

반복문이 몇번 실행될지 알 수 없거나 정해지지 않은 경우 위 포맷에 따라 작성하면 된다. 다시말해, 코드블럭 안에서 조건을 한번 또는 여러번 검사해야 하는 경우에 사용하면 된다. 

let time = 0

console.log('loading...')
while(time < 1000000000){
  if(time === 700000000){
    break
  }
  time++
}
console.log('finished loading!')

해당 반복문은 일정시간동안 디레이를 주고, 디레이가 끝나면 반복문을 종료하고 다른 프로그램을 실행한다. 

 

* 다음 반복으로 넘어가기(continue)

continue 지시자는 break 문의 가벼운 버전이다. continue 는 break 와 달리 전체 반복문을 종료시키는 것이 아니라 현재 실행중인 반복문(이터레이션)을 도중에 종료하고 강제로 다음 반복문(이터레이션)을 실행시키도록 한다. 물론 조건을 통과한 경우에 한해서 말이다. 

for (let i = 0; i < 10; i++) {

  // 조건이 참이라면 남아있는 본문은 실행되지 않습니다.
  if (i % 2 == 0) continue;

  alert(i); // 1, 3, 5, 7, 9가 차례대로 출력됨
}

해당 반복문은 continue 를 이용하여 짝수는 출력하지 않고, 홀수만 화면에 출력한다. i 가 짝수이면 continue 가 반복문 실행을 도중에 멈추고 그 다음 이터레이션을 실행한다. 

for (let i = 0; i < 10; i++) {

  if (i % 2) {
    console.log(i)
  }

}

해당 반복문도 이전 코드와 동일하게 홀수만 출력한다. 하지만 이렇게 작성하면 코드블럭이 중첩된다. 즉, 중괄호가 두번 사용되기 때문에 조건문 안의 코드가 길어지면 코드 가독성이 떨어질 수 있다. 결론적으로 continue 는 중첩을 줄이는데 도움이 된다. 

 

* 삼항연산자와 break/continue 

if (i > 5) {
  alert(i);
} else {
  continue;
}

해당 코드를 삼항연산자로 다시 작성하면 아래와 같다. 

(i > 5) ? alert(i) : continue; // 여기에 continue를 사용하면 안 됩니다.

하지만 해당 코드는 문법에러를 일으킨다. 즉, 삼항연산자에는 break 나 continue 를 사용할 수 없다. 그러므로 조건문을 삼항연산자로 작성할때는 주의가 필요하다. 

 

* 중첩 반복문

let letter = 'A'
let str = ''

for(let i = 0; i<3; i++){
  for(let j = 0; j< 7; j++){
    str += letter
  }
  str+='\n'
}
console.log(str)

중첩 반복문은 반복문 안에 다른 반복문이 들어가는 형태이다. 현재 코드는 아래와 같이 3개의 행과 7개의 열로 이루어진 "A" 패턴을 만든다. 

 

* break/continue 와 레이블(label)

여러개의 중첩 반복문을 한번에 빠져나와야 하는 경우가 종종 있다. 

for (let i = 0; i < 3; i++) {

  for (let j = 0; j < 3; j++) {

    let input = prompt(`(${i},${j})의 값`, '');

    // 여기서 멈춰서 아래쪽의 `완료!`가 출력되게 하려면 어떻게 해야 할까요?
  }
}

alert('완료!');

해당 반복문은 i, j 값을 반복적으로 증가시키면서 (0, 0)부터 (2, 2)까지의 좌표를 입력하도록 사용자 입력창을 띄워준다. 이때 사용자가 [취소]버튼을 클릭하거나 아무값도 입력하지 않은 경우에는 더이상 사용자 입력을 받지 않고, 종료하고 싶다. 물론 break 문을 사용하면 되지만, 안쪽에 있는 반복문만 빠져나올뿐 전체 반복문을 빠져나오지는 못한다. 

labelName: for (...) {
  ...
}

이러한 경우에 바깥쪽 for 문에 레이블(label)을 사용할 수 있다. 

outer: for (let i = 0; i < 3; i++) {

  for (let j = 0; j < 3; j++) {

    let input = prompt(`(${i},${j})의 값`, '');

    // 사용자가 아무것도 입력하지 않거나 Cancel 버튼을 누르면 두 반복문 모두를 빠져나옵니다.
    if (!input) break outer; // (*)

    // 입력받은 값을 가지고 무언가를 함
  }
}
alert('완료!');

반복문 안에서 break [레이블 이름]을 사용하면 레이블(label)이 표기된 반복문을 빠져나올수 있다. 반복문을 빠져나오자마자 "완료" 문구가 화면에 출력된다. 

break label; // 아래 for 문으로 점프할 수 없습니다.

label: for (...)

하지만 레이블을 사용한다고 해서 맘대로 다른 코드 위치로 점프할 수는 없다. 위 예시처럼 하는 것은 불가능하다. break/continue 는 반복문 안에서만 사용할 수 있으며, 레이블은 반드시 break/continue 위에서 정의해야 한다. 

 

연습과제 1

let i = 7;

while (i) {
  alert( i-- );
}

해당 반복문을 실행할때 마지막에 출력되는 값은 무엇인지 알아보세요!

 

연습과제 2

let i = 0;
while (++i < 5) alert( i );

해당 코드를 실행할때 순차적으로 출력되는 값을 예상해보고, 조건문으로 다시 작성해보세요! (코드실행 X)

 

연습과제 3

let i = 0;
while (i++ < 5) alert( i );

해당 코드를 실행할때 순차적으로 출력되는 값을 예상해보고, 조건문으로 다시 작성해보세요! (코드실행 X)

 

연습과제 4

for (let i = 0; i < 5; i++) alert( i );

해당 코드를 실행할때 순차적으로 출력되는 값을 예상해보고, 조건문으로 다시 작성해보세요! (코드실행 X)

 

연습과제 5

for (let i = 0; i < 5; ++i) alert( i );

해당 코드를 실행할때 순차적으로 출력되는 값을 예상해보고, 조건문으로 다시 작성해보세요! (코드실행 X)

 

연습과제 6

for 반복문을 이용하여 1부터 n 까지 숫자 중 홀수만 출력해보세요! 

1. n은 사용자로부터 입력받는다.
2. n이 1보다 작거나 같으면 1보다 큰수를 입력받을때까지 프롬프트 창을 띄워서 계속 사용자 입력을 받도록 한다.
3. 사용자가 아무런 값도 입력하지 않거나 취소버튼을 누르면 프롬프트 창은 더이상 띄우지 않고 홀수도 찾지 않고 종료한다.

위 조건을 만족시키도록 프로그램을 작성하세요.

 

연습과제 7

소수(prime number)는 자신보다 작은 두 개의 자연수를 곱하여 만들 수 없는 1보다 큰 자연수이다. 다시 말해 1 자기 자신으로만 나눌 수 있는 자연수를 소수라고 부른다. 예를 들어 5 2, 3, 4로 나눌 수 없기 때문에 소수이다. 5를 이들 숫자로 나누면 나머지가 있기 때문이다. 2부터 n까지의 숫자 중 소수만 출력해주는 코드를 작성해보자! n = 10이라면 결과는 2, 3, 5, 7이 된다.

1. n은 사용자로부터 입력받는다.
2. n이 2보다 작거나 같으면 2보다 큰수를 입력받을때까지 프롬프트 창을 띄워서 계속 사용자 입력을 받도록 한다.
3. 사용자가 아무런 값도 입력하지 않거나 취소버튼을 누르면 프롬프트 창은 더이상 띄우지 않고 소수도 찾지 않고 종료한다.

위 조건을 만족시키도록 프로그램을 작성하세요.

 

연습과제 8

중첩 반목문을 이용하여 콘솔창에 아래와 같은 픽셀아트 패턴을 만들어보세요!

○●

콘솔창 출력화면

 

 

연습문제 9

100보다 작은 자연수 중에서 5의 배수를 아래와 같이 거꾸로 출력하는 코드를 작성해보세요!

 

 

728x90