본문 바로가기

Go

[묘공단] Tucker의 Go 언어 프로그래밍 8장~11장

8장 상수

  • 상수는 말그대로 변하지 않는 값을 말한다. 원주율(3.141592...)같이 변하지 않는 값을 정의할때 사용한다.

 

 

 

  • 아래와 같이 상수 C를 선언한후에 값을 바꾸려고 하면 오류가 발생한다.
//ch8/ex8.1/ex8.1.go
package main

import "fmt"

func main() {
	const C int = 10 // ❶ 상수 선언

	var b int = C * 20 // ❷ 대입문 우변에 값으로 동작합니다.
	C = 20             // ❸ 에러 발생 - 상수는 대입문 좌변에 올 수 없습니다.
	fmt.Println(&C)    //
}

 

 

//ch8/ex8.2/ex8.2.go
package main

import "fmt"

func main() {
	const PI1 float64 = 3.141592653589793238 // ❶
	var PI2 float64 = 3.141592653589793238   // ❷

	// PI1 = 4  // ❸
	PI2 = 4 // ➍

	fmt.Printf("원주율: %f\n", PI1)
	fmt.Printf("원주율: %f\n", PI2) // ➎
}

 

  • 아래 소스는 , 상수값에 코드를 부여하여 0,1,2 같은 숫자값보다는 Pig, Cow,Chicken 을 사용하여, 좀 더 가독성이 좋은 코드를 작성할수있다.
//ch8/ex8.3/ex8.3.go
package main

import "fmt"

// ❶ 상수값에 코드를 부여합니다.
const Pig int = 0
const Cow int = 1
const Chicken int = 2

// ❸ 코드값에 따라서 다른 텍스트를 출력합니다.
func PrintAnimal(animal int) {
	if animal == Pig {
		fmt.Println("꿀꿀")
	} else if animal == Cow {
		fmt.Println("음메")
	} else if animal == Chicken {
		fmt.Println("꼬끼오")
	} else {
		fmt.Println("...")
	}
}

func main() {
	// ❷ PrintAnimal() 함수를 호출하여 동물 소리를 출력합니다.
	PrintAnimal(Pig)
	PrintAnimal(Cow)
	PrintAnimal(Chicken)
}

 

  • 아래 소스와 같이 상수를 그룹지어 한번에 표시할수있다. iota를 사용하면 0부터 1씩 증가하면서 자동으로 값이 지정된다.
package main
import "fmt"

const (
	Pig = iota
	Cow
	Chicken
)

func PrintAnimal(animal int) {
	if animal == Pig {
		fmt.Println("꿀꿀")
	} else if animal == Cow {
		fmt.Println("음메")
	} else if animal == Chicken {
		fmt.Println("꼬끼오")
	} else {
		fmt.Println("...")
	}
}

func main() {
	PrintAnimal(Pig)
	PrintAnimal(Cow)
	PrintAnimal(Chicken)
}

 

 

 

 

  • PI 처럼 타입없이 상수를 사용하면, 해당 상수가 변수에 복사될때 타입이 정해지기때문에 범용적으로 사용가능하다.
  • FloatPI는 float64 로 타입이 정해져있어서, float64형의 변수에만 사용가능하다.
//ch8/ex8.4/ex8.4.go
package main

import "fmt"

const PI = 3.14              // ❶ 타입 없는 상수
const FloatPI float64 = 3.14 // ❷ float64 타입 상수

func main() {
	var a int = PI * 100      // ❸ 오류 발생하지 않습니다.
	var b int = FloatPI * 100 // ➍ 타입 오류 발생

	fmt.Println(a)
	fmt.Println(b)
}

 

 

9장 if 문

  • if문은 조건에 따라 분기하는 구문이다.

 

  • 아래 소스에서 light값에 따라서 if문으로 해당 값에 맞는 출력을 수행한다. 
//ch9/ex9.1/ex9.1.go
package main

import "fmt"

func main() {
	light := "red"

	if light == "green" {
		fmt.Println("길을 건넌다")
	} else {
		fmt.Println("대기한다")
	}
}

 

 

 

  • 아래 소스와 같이 if, else 를 사용하여 여러가지로 수행하게 할수있다.
  • 온도가 28도를 초과하면, 에어컨을 켜고, 3도이하면 히터를 켜고 , 나머지 상황에서는 대기하는 코드이다.
//ch9/ex9.2/ex9.2.go
package main

import "fmt"

func main() {
	temp := 33 // ❶

	if temp > 28 { // ❷
		fmt.Println("에어컨을 켠다")
	} else if temp <= 3 {
		fmt.Println("히터를 켠다")
	} else {
		fmt.Println("대기한다")
	}
}

 

 

  • if안의 조건에서 && || 연산자를 사용하여, 여러가지 조건을 넣을수있다. (&& 는 AND, || 는 OR) 
  • && 는 양쪽이 모두 true인 경우에만 true 이고, || 는 한쪽만 true여도 true값을 가진다.
//ch9/ex9.3/ex9.3.go
package main

import "fmt"

func main() {
	var age = 22 // ❶

	if age >= 10 && age <= 15 {
		// age가 10 이상 15 이하인 경우
		fmt.Println("You are young")
	} else if age > 30 || age < 20 {
		// age가 30보다 크거나 20보다 작은 경우. 즉 20대가 아닌 경우
		fmt.Println("You are not 20s")
	} else { // ❷
		fmt.Println("Best age of your life")
	}
}

 

 

  • 아래는 쇼트서킷의 경우이다.
  • 쇼트서킷은 &&문에서 좌변이 false이면 우변은 무시하고, ||문에서 좌변이 true이면 우변이 무시된다. 
//ch9/ex9.4/ex9.4.go
package main

import "fmt"

var cnt int = 0

func IncreaseAndReturn() int {
	fmt.Println("IncreaseAndReturn()", cnt)
	cnt++
	return cnt
}

func main() {
	if false && IncreaseAndReturn() < 5 { // ❶ 함수가 호출되지 않습니다.
		fmt.Println("1 increase")
	}
	if true || IncreaseAndReturn() < 5 { // ❷ 함수가 호출되지 않습니다.
		fmt.Println("2 increase")
	}

	fmt.Println("cnt:", cnt)
}

 

 

  • 아래는 중첩 if문의 예이다. 중첩 if문은 if문안에 다시 if문을 사용할수있는것을 말한다.
//ch9/ex9.5/ex9.5.go
package main

import "fmt"

// 친구 중 부자가 있는가 반환 - 무조건 true 반환
func HasRichFriend() bool {
	return true
}

// 같이 간 친구 숫자를 반환 - 무조건 3을 반환
func GetFriendsCount() int {
	return 3
}

func main() {
	price := 35000

	if price > 50000 { // ❶
		if HasRichFriend() {
			fmt.Println("앗 신발끈이 풀렸네")
		} else {
			fmt.Println("나눠내자")
		}
	} else if price >= 30000 && price <= 50000 { // ❷
		if GetFriendsCount() > 3 { // ❸
			fmt.Println("어이쿠 신발끈이..")
		} else {
			fmt.Println("사람도 얼마 없는데 나눠내자")
		}
	} else {
		fmt.Println("오늘은 내가 쏜다")
	}
}

 

 

  • Go에서는 if문에서 조건을 검사하기전에 초기문을 넣을수있다. 초기문은 검사에 사용할 변수를 초기화할때 사용한다.
  • 해당 변수는 if문안에서만 유효하다.
//ch9/ex9.6/ex9.6.go
package main

import "fmt"

func getMyAge() (int, bool) {
	return 33, true
}

func main() {

	if age, ok := getMyAge(); ok && age < 20 {
		fmt.Println("You are young", age)
	} else if ok && age < 30 {
		fmt.Println("Nice age", age) // age 값에 접근가능하다 - ❶
	} else if ok {
		fmt.Println("You are beautiful", age)
	} else {
		fmt.Println("Error")
	}

	fmt.Println("Your age is", age) // Error - age는 소멸됐다 - ❷
}

 

 

10장 switch 문

 

  • switch문은 비교하려는 값에 따라 여러가지로 분기할때 if문보다는 좀더 간결하게 표현할수있다.
  • 사용방법은 아래와 같다.
//ch10/ex10.1/ex10.1.go
package main

import "fmt"

func main() {

	a := 3

	switch a { // ❶
	case 1:
		fmt.Println("a == 1")
	case 2:
		fmt.Println("a == 2")
	case 3: // ❷
		fmt.Println("a == 3")
	case 4:
		fmt.Println("a == 4")
	default:
		fmt.Println("a > 4")
	}
}

 

 

  • 아래 소스는 if문을 사용하여 날짜별로 다른 메세지를 출력하는 소스이다. 
//ch10/ex10.2/ex10.2.go
package main

import "fmt"

func main() {

	day := 3

	if day == 1 {
		fmt.Println("첫째 날입니다")
		fmt.Println("오늘은 팀미팅이 있습니다.")
	} else if day == 2 {
		fmt.Println("둘째 날입니다")
		fmt.Println("새로운 팀원 면접이 있습니다.")
	} else if day == 3 {
		fmt.Println("셋째 날입니다.")
		fmt.Println("설계안을 확정하는 날입니다.")
	} else if day == 4 {
		fmt.Println("넷째 날입니다.")
		fmt.Println("예산을 확정하는 날입니다.")
	} else if day == 5 {
		fmt.Println("다섯째 날입니다.")
		fmt.Println("최종 계약하는 날입니다.")
	} else {
		fmt.Println("프로젝트를 진행하세요")
	}
}

 

 

 

  • 위의 if문을 사용한 소스를 아래와 같이 switch문으로 바꾸었다. 소스가 훨씬 깔끔해젼다.
//ch10/ex10.3/ex10.3.go
package main

import "fmt"

func main() {

	day := 3

	switch day {
	case 1:
		fmt.Println("첫째 날입니다")
		fmt.Println("오늘은 팀미팅이 있습니다.")
	case 2:
		fmt.Println("둘째 날입니다")
		fmt.Println("오늘은 면접이 있습니다.")
	case 3:
		fmt.Println("셋째 날입니다.")
		fmt.Println("설계안을 확정하는 날입니다.")
	case 4:
		fmt.Println("넷째 날입니다.")
		fmt.Println("예산을 확정하는 날입니다.")
	case 5:
		fmt.Println("다섯째 날입니다.")
		fmt.Println("최종 계약하는 날입니다.")
	default:
		fmt.Println("프로젝트를 진행하세요")
	}
}

 

 

  • 아래 소스와 같이 case에 여러값을 비교할수있다.
//ch10/ex10.4/ex10.4.go
package main

import "fmt"

func main() {

	day := "thursday"

	switch day {
	case "monday", "tuesday": // ❶
		fmt.Println("월, 화요일은 수업 가는 날입니다.")
	case "wednesday", "thursday", "friday": // ❷
		fmt.Println("수, 목, 금요일은 실습 가는 날입니다.")
	}
}

 

  • switch문은 비굣값과 case문이 같아지는 경우를 찾는 구문이기때문에 비굣값을 true로 할경우 case문의 조건문이 true가 되는경우 실행되게 할수있다. 아래 소스와 같이 case문이 true인경우 해당 부분이 실행된다.  (아래 소스의 경우 비굣값의 default는 true이므로 생략가능하다.)
//ch10/ex10.5/ex10.5.go
package main

import "fmt"

func main() {
	temp := 18

	switch true {
	case temp < 10 || temp > 30:
		fmt.Println("바깥 활동하기 좋은 날씨가 아닙니다.")
	case temp >= 10 && temp < 20:
		fmt.Println("약간 추울 수 있으니 가벼운 겉옷을 준비하세요")
	// 이미 두 번째 case를 실행했기 때문에 검사하지 않습니다.
	case temp >= 15 && temp < 25:
		fmt.Println("야외활동 하기 좋은 날씨입니다")
	default:
		fmt.Println("따뜻합니다.")
	}
}

 

  • if문의 경우처럼 switch문도 초기문을 넣을수있다. 아래 소스처럼 age값을 초기화한후에 해당 age값을 비교하여 case문에 해당 값에 따라 다른 메세지를 출력한다. if문의 경우처럼 해당 age값은 switch문안에서만 유효하다.
//ch10/ex10.6/ex10.6.go
package main

import "fmt"

func getMyAge() int {
	return 22
}

func main() {
	switch age := getMyAge(); age { // getMyAge() 결과값 비교 ❶
	case 10:
		fmt.Println("Teenage")
	case 33:
		fmt.Println("Pair 3")
	default:
		fmt.Println("My age is", age) // age값 사용
	}

	fmt.Println("age is", age) // error - age 변수는 사라졌습니다. ❷
}

 

 

  • 위의 소스들을 참고하여, 아래 소스에서는 switch문에서 변수를 초기화하고, 비굣값을  true로 설정하여, case문의 조건이 참일경우 해당 case문이 실행된다.
//ch10/ex10.7/ex10.7.go
package main

import "fmt"

func getMyAge() int {
	return 22
}

func main() {
	// age 변수 선언 및 초기화
	switch age := getMyAge(); true { // ❶
	case age < 10:
		fmt.Println("Child")
	case age < 20:
		fmt.Println("Teenager")
	case age < 30:
		fmt.Println("20s")
	default:
		fmt.Println("My age is", age) // age값 사용
	}
}

 

  • 아래는 const열거값에 따라 수행되는 로직을 변경하는 switch문의 예제이다.  colorToString()함수는 해당 열거값에 해당하는 문자열을 리턴한다.
//ch10/ex10.8/ex10.8.go
package main

import "fmt"

type ColorType int // 별칭 ColorType을 선언하고 Const 열거값 정의 ❶
const (
	Red ColorType = iota
	Blue
	Green
	Yellow
)

// 각 ColorType 열거값에 따른 문자열을 반환하는 함수
func colorToString(color ColorType) string { // ❷
	switch color {
	case Red:
		return "Red"
	case Blue:
		return "Blue"
	case Green:
		return "Green"
	case Yellow:
		return "Yellow"
	default:
		return "Undefined"
	}
}

func getMyFavoriteColor() ColorType {
	return Blue
}

func main() {
	fmt.Println("My favorite color is", colorToString(getMyFavoriteColor()))
}

 

 

  • 일반적으로 다른언에서는 switch문의 각 case종료시 break문을 사용해야 다음 case문으로 코드가 이어서 실행되지 않는다.  하지만 Go 에서는 break문의 사용과 상관없이 다음 case로 실행되지 않는다. 
//ch10/ex10.9/ex10.9.go
package main

import "fmt"

func main() {

	a := 3

	switch a { // ❶
	case 1:
		fmt.Println("a == 1")
		break
	case 2:
		fmt.Println("a == 2")
		break
	case 3: // ❷
		fmt.Println("a == 3")
	case 4:
		fmt.Println("a == 4")
	default:
		fmt.Println("a > 4")
	}
}

 

 

  • 아래 소스와 같이 fallthrough 을 사용하면 다음의 case문까지 실행이 된다. (책에서는 해당 키워드를 권장하지는 않는다.)
//ch10/ex10.10/ex10.10.go
package main

import "fmt"

func main() {

	a := 3

	switch a {
	case 1:
		fmt.Println("a == 1")
		break
	case 2:
		fmt.Println("a == 2")
	case 3:
		fmt.Println("a == 3")
		fallthrough //
	case 4:
		fmt.Println("a == 4")
	case 5:
		fmt.Println("a == 5")
	default:
		fmt.Println("a > 4")
	}
}

 

 

11장 for 문

  • 다른 언어들과 같이 for문의 기본사용법은 초기문;조건문;후처리 의 구조로 되어있다. 각 부분은 생략가능하면 모두 생략하면 조건문만 존재해도 가능하다. (조건문이 true)라면 조건문도 생략가능하다.
//ch11/ex11.1/ex11.1.go
package main

import "fmt"

func main() {
	for i := 0; i < 10; i++ { // ❶ 초기문; 조건문; 후처리
		fmt.Print(i, ", ") // ❸ i 값을 출력합니다.
	}

	//  fmt.Println(i)              // ❸ Error - i는 이미 사라졌습니다.
}

 

 

  • 아래 소스는 for문에 조건이 없기때문에 무한루프이다. 계속 루프를 돌면서 시간값을 출력하는 소스이다.
//ch11/ex11.2/ex11.2.go
package main

import (
	"fmt"
	"time"
)

func main() {
	i := 1
	for {
		time.Sleep(time.Second)
		fmt.Println(i)
		i++
	}
}

 

  • 아래의 문장은 for문에서 continue, break를 사용한 예제이다.
  • for문을 사용하여, 숫자를 입력받는 무한루프인데, 입력받은 값이 숫자가 아니면 키보드 버퍼를 비우고 continue문을 호출하여, 다시 입력을 받고, 입력받은 값이 짝수일경우 break가 실행되어 for문을 종료하는 소스이다. 
//ch11/ex11.3/ex11.3.go
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문안에 다시 for문을 사용할수있다.
//ch11/ex11.4/ex11.4.go
package main

import "fmt"

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

 

 

  • 중첩for문의 다른 예제이다. 
//ch11/ex11.5/ex11.5.go
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() // ❹ 줄바꿈합니다.
	}
}

 

  • 중첩for문과 break문을 사용한 구구단을 출력하는 소스이다.  좀더 간단하게 표현가능하지만 break문의 사용법때문에 이렇게 표현하였다.
//ch11/ex11.6/ex11.6.go
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문이 종료되었습니다.")
}

 

  • 아래 소스는 구구단에서 두수의 곱이 45가 되었을경우 루프를 탈출하는 소스이다. found라는 플래그 값을 두어, 두수의 곱이 45가 되면 found를 true로 만들어 루프를 탈출한다. 
//ch11/ex11.7/ex11.7.go
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)
}

 

 

  • 위의 소스를 아래와같이 OuterFor라는 레이블을 사용하여 해당 조건이 만족할경우 break 레이블이름을 사용하여 해당 레이블에서 제일 먼저 포함된 for문을 종료하게 된다. 하지만 책에서는 권장하지 않는다.
//ch11/ex11.8/ex11.8.go
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)
}

 

 

  • 아래 소스는 함수를 사용하여 위소스처럼 flag변수를 두거나 레이블을 사용하지 않고, 깔끔하게 처리할수있다. 
//ch11/ex11.9/ex11.9.go
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)
}