본문으로 바로가기

[Golang] for문

category Programming/Go 2022. 11. 11. 23:44

Contents

    for문 동작 원리

    • Go언어는 반복문으로 for문 하나만 지원한다.
      • 하지만 여러가지 형태가 있기 때문에 적재적소에 잘 사용해야 한다.

    형식

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

    동작 순서

    1. 초기문이 먼저 실행됨.
    2. 조건문을 검사함.
      • if 조건문이 true?
        1. for문 { } 안쪽 코드 블록 수행
        2. 후처리 구문 실행
      • if 조건문이 false?
        1. for문 종료

    다양한 형태

    1️⃣ 초기문 생략

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

    초기문을 생략해도 ;를 붙여서 조건문 자리를 표시해줘야 한다.

    2️⃣ 후처리 생략

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

    후처리를 생략해도 조건문 뒤에 ;를 붙여줘야 한다.

    3️⃣ 조건문만 있는 경우

    형식 1

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

    형식 2

    for 조건문 {
    	코드 블록
    }
    

    4️⃣ 무한 루프

    형식 1

    for true {
    	코드 블록
    }
    

    조건문이 true이면 무한 루프가 된다.

    형식 2

    for {
    	코드 블록
    }
    

    switch문과 마찬가지로 true를 생략할 수 있다.

    continue와 break

    • continue : 이후 코드 블록을 수행하지 않고 곧바로 후처리를 하고 조건문 검사부터 다시한다.
    • break : for문에서 곧바로 빠져 나온다.

    예제

    package main
    
    import (
    	"bufio"
    	"fmt"
    	"os"
    )
    
    func main() {
    	stdin := bufio.NewReader(os.Stdin)
    	for { // ❶ 무한 루프
    		fmt.Println("입력하세요")
    		var number int
    		_, err := fmt.Scanln(&number) // ❷ 한줄 입력을 받습니다.
    		if err != nil {               // ❸ 숫자가 아닌 경우
    			fmt.Println("숫자를 입력하세요")
    
    			// 키보드 버퍼를 비웁니다.
    			stdin.ReadString('\\n') // ❹ 키보드 버퍼를 지워줍니다.
    			continue               // ➎ ❶ 로 돌아갑니다
    		}
    		fmt.Printf("입력하신 숫자는 %d입니다\\n", number)
    		if number%2 == 0 { // ➏ 짝수 검사를 합니다.
    			break // ➐ for문을 종료합니다.
    		}
    	}
    	fmt.Println("for문이 종료되었습니다.")
    }

    중첩 for문

    for문을 중첩해서 사용할 수 있다.

    예제 1

    package main
    
    import "fmt"
    
    func main() {
    	for i := 0; i < 3; i++ { // ❶ 3번 반복합니다.
    		for j := 0; j < 5; j++ { // ❷ 5번 반복합니다.
    			fmt.Print("*") // ❸ * 를 출력합니다.
    		}
    		fmt.Println() // ❹ 줄바꿈합니다.
    	}
    }
    중첩 반복문을 사용하면 연산량이 크게 증가되므로 사용에 주의해야 한다.

    예제 2

    package main
    
    import "fmt"
    
    func main() {
    	for i := 0; i < 5; i++ { // ❶ 5번 반복합니다.
    		for j := 0; j < i+1; j++ { // ❷ 현재 i값+1만큼 반복합니다.
    			fmt.Print("*") // ❸ *를 출력합니다.
    		}
    		fmt.Println() // ❹ 줄바꿈합니다.
    	}
    }

    예제 3 continue와 break를 사용

    package main
    
    import "fmt"
    
    func main() {
    	dan := 2
    	b := 1
    	for {
    		for {
    			fmt.Printf("%d * %d = %d\\n", dan, b, dan*b)
    			b++
    			if b == 10 { // ❶
    				break // 안쪽 for문을 종료합니다.
    			}
    		}
    		b = 1
    		dan++
    		if dan == 10 { // ❷
    			break // 바깥쪽 for문을 종료합니다.
    		}
    	}
    	fmt.Println("for문이 종료되었습니다.")
    }
    • for문을 중첩하여 구구단을 출력한다.

    중첩 for문과 break, 레이블

    앞의 예제들은 break가 속한 for문에서만 빠져나온다.

    모든 for문을 빠져나가고 싶을 때는 어떻게 해야 할까?

    예제 1 플래그 (Flag) 변수 사용

    package main
    
    import "fmt"
    
    func main() {
    	a := 1
    	b := 1
    
    	found := false
    	for ; a <= 9; a++ {
    		for b = 1; b <= 9; b++ {
    			if a*b == 45 {
    				found = true // ❶ 찾았음을 표시하고 break
    				break
    			}
    		}
    		if found { // ❷ 바깥쪽 for문에서 찾았는지 검사해서 break
    			break
    		}
    	}
    	fmt.Printf("%d * %d = %d\\n", a, b, a*b)
    }
    • 1~9 사이의 두 수를 곱했을 때 45가 되는 수를 찾는다.
    • 안쪽 for문에서 두 수의 곱이 45가 되는 경우를 찾았으면 안쪽 for문을 종료한다.
      • 이 때 바깥쪽의 for문은 종료되지 않았으므로 found가 ture인지 검사하고 for문을 종료해야 한다.

    이런 형태로 boolean 변수를 사용하는 것을 플래그(Flag) 변수라고한다.

    그러나 이런 플래그 변수 사용하는 것은 번거로울 수 있음. (삼중, 사중 for문 이면 복잡..)

    예제 2 레이블

    package main
    
    import "fmt"
    
    func main() {
    	a := 1
    	b := 1
    
    OuterFor:
    	for ; a <= 9; a++ {
    		for b = 1; b <= 9; b++ {
    			if a*b == 45 {
    				break OuterFor
    			}
    		}
    	}
    	fmt.Printf("%d * %d = %d\\n", a, b, a*b)
    }

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

    • 레이블은 레이블 이름을 적고 콜론 : 을 적어서 정의한다.

    그러나 레이블 방법은 편리할 수 있으나 다음의 단점이 존재한다.

    1. 혼동을 불러일으킬 수 있음.
    2. 예기치 못한 버그가 발생할 수 있음.
    따라서 되도록 플래그를 사용하고 레이블은 꼭 필요한 경우에만 사용하기를 권장한다.

    클린코드

    1. 중첩된 내부 로직을 함수로 묶어서 중첩을 줄이고
    2. 플래그 변수나 레이블 사용을 최소화 해야한다.

    예제

    package main
    
    import "fmt"
    
    func find45(a int) (int, bool) { // ❶ 곱해서 45가 되는 값을 찾는다
    	for b := 1; b <= 9; b++ {
    		if a*b == 45 {
    			return b, true
    		}
    	}
    	return 0, false
    }
    
    func main() {
    	a := 1
    	b := 0
    
    	for ; a <= 9; a++ {
    		var found bool
    		if b, found = find45(a); found { // ❷ 함수 호출
    			break
    		}
    	}
    	fmt.Printf("%d * %d = %d\\n", a, b, a*b)
    }

    'Programming > Go' 카테고리의 다른 글

    [Golang] 구조체 (Structure)  (1) 2022.11.13
    [Golang] 배열 (Array)  (0) 2022.11.12
    [Golang] Switch문  (0) 2022.11.06
    [Golang] if문  (0) 2022.11.06
    [Golang] 상수 (Const)  (0) 2022.10.29