chaenii 2022. 1. 15. 01:17

본 게시물은 Tucker의 Go언어 프로그래밍을 참고해 작성한 게시물입니다.

http://www.yes24.com/Product/Goods/99108736

 

Tucker의 Go 언어 프로그래밍 - YES24

게임 회사 서버 전문가가 알려주는 Go 언어를 내 것으로 만드는 비법구글이 개발한 Go는 고성능 비동기 프로그래밍에 유용한 언어이다. 『Tucker의 Go 언어 프로그래밍』은 Go 언어로 ‘나만의 프로

www.yes24.com

 

for문 동작 원리

for 초기문; 조건문; 후처리 {
    코드 블록   // 조건문이 true인 경우 수행된다.
}

조건문이 true인 경우, 코드블럭을 수행하고 후처리를 한다. ( 조건문 결과 -> { } 코드 블록 수행 -> 후처리 )

조건문이 false인 경우, 후처리 없이 for문을 종료한다.

 

초기문 생략 

for ; 조건문; 후처리 {
    코드 블럭 
}

 

후처리 생략

for 초기문; 조건문; {
    코드 블럭
}

 

조건문만 있는 경우

for ; 조건문; {
    코드 블럭
}

for 조건문 {
    코드 블럭
}

 

무한 루프

// 무한루프
for true {
    코드 블럭
}

for {
    코드 블럭
}

 

continue와 break

continue와 break은 반복문을 제어하는 키워드이다.

continue는 이후 코드 블록을 수행하지 않고 곧바로 후처리를 하고 조건문 검사부터 다시 한다.

break문은 for문을 곧바로 빠져나온다.

for i := 0; i < 10; i++ {
    if i == 3{
       continue // 후처리로 건너뛰기
    }
    if i == 6{
       break    // for문 종료
    }
 }
stdin := bufio.NewReader(os.Stdin)
for {                         
    fmt.Println("입력하세요.")
    var num int
    _, err := fmt.Scanln(&num)
    if err != nil { // 숫자가 아닌 경우
        fmt.Println("숫자를 입력하세요.")

        stdin.ReadString('\n') // 키보드 버퍼를 지워준다.
        continue               // 다시 무한 루프 초기로 돌아간다.
    }

    fmt.Printf("입력하신 숫자는 %d입니다.\n", num)
    if num%2 == 0 { // 짝수 검사
        break // for문을 종료한다.
    }
}
fmt.Println("for문이 종료됐습니다.")

 

  1. 무한 루프 시작
  2. 키보드로 부터 숫자를 입력받는다.
  3. 입력이 숫자가 아닌 경우 err에 에러값이 들어간다.
  4. 에러 발생 시, 문자열을 다시 읽어 키보드 버퍼를 지운다.
  5. continue를 통해 이후 코드 블록을 건너뛴다.
  6. 다음을 입력받는다.
  7. 짝수인지 검사한다.
  8. 짝수라면 break로 for문을 종료한다.

 

중첩 for문

한 번 이상 중첩해 사용한 for문을 중첩 for문이라고 한다.

for i := 0; i < 5; i++ {
    for j := 0; j < i+1; j++ {
        fmt.Print("*")
    }
    fmt.Println()
}

-----------------------------------

*
**
***
****
*****

 

중첩 for문에서 break와 continue를 사용하면 continue와 break가 속한 코드 블록의 for문 끝으로 가거나 곧바로 for문을 빠져나가게 된다.

i := 2
j := 1

for {
    for {
        if j == 10 {
            break // 안쪽 for문을 종료
        }
        fmt.Printf("%d x %d = %d\n", i, j, i*j)
        j++
    }
    j = 1
    i++
    if i == 10 {
        break     // 바깥쪽 for문을 종료
    }
}

 

 

중첩 for문과 break, 레이블

앞서 예제에서 보면 중첩 for문에서 break를 사용하면 break가 속한 for문에서만 빠져나온다. 모든 for문을 빠져나가고 싶을 때는 어떻게 해야할까?

 

#1 boolean 

1~9사이에 두 수를 곱했을 때 45가 되는 수를 찾는다. 안쪽 for문에서 두 수의 곱이 45가 되는 경우를 찾으면 found를 true로 바꾸고 break해 안쪽 for문을 종료한다. 

이때 바깥쪽 for문은 종료되지 않았기 때문에 found가 true인지 검사해서 바깥쪽 for문까지 검사해야한다. 

이런 형태로 불리언 변수를 사용하는 것을 깃발처럼 올라갔는지 내려갔는지를 표시한다고 해서 플래그(flag) 변수라고 한다.

a := 1
b := 1
found := false
for ; a < 10; a++ {
    for b = 1; b < 10; b++ {
        if a*b == 45 {
            fmt.Println(a, b)
            found = true // 찾았음을 표시하고 break
            break
        }
    }
    if found {
        break // 바깥쪽 for문에서 찾았는지 검사해서 break
    }
}

하지만 flag는 삼중 사중 for문이면 더 복잡할 것이다.

 

#2 label

for문을 시작할 때 레이블을 정의하고 break할 때 앞서 정의한 레이블을 적어주면 그 레이블에서 가장 먼저 속한 for까지 모두 종료하게 된다.

	a := 1
	b := 1
OuterFor: // 레이블 정의
	for ; a < 10; a++ {
		for b = 1; b < 10; b++ {
			if a*b == 45 {
				fmt.Println(a, b)
				break OuterFor // 레이블에 가장 먼저 포함된 for문까지 종료
			}
		}
	}

하지만 레이블을 사용하는 방법은 혼동을 불러일으킬 수 있고 잘못 사용하면 버그가 발생할 수 있다.

되도록 플래그를 사용하고 레이블은 필요한 경우에 사용하는 것이 좋다.

클린 코드를 지향하려면 중첩된 내부 로직을 함수로 묶어 중첩을 줄이고, 플래그 변수나 레이블 사용을 최소화해야한다.

 

개선된 코드

func find45(a int) (int, bool) {
	for b := 1; b <= 9; b++ {
		if a*b == 45 {
			return b, true
		}
	}
	return 0, false
}

func main() {

	for a := 1; a <= 9; a++ {
		if b, found := find45(a); found {
			fmt.Println(a, b)
			break
		}
	}
}
반응형