본문 바로가기

Go

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

  • 3장 Hello Go World
  • 4장 변수
  • 5장 fmt패키지를 이용한 텍스트 입출력
  • 6장 연산자
  • 7장 함수

 

 

 

 

3장 Hello Go World

 

(https://madnight.github.io/githut/#/pull_requests/2023/4)

 

 

 

package main
/* 
go언어의 모든 코드는 반드시 package 선언으로 시작해야한다. 
main package는 프로그램 시작점을 포함하는 특별한 package이다.
*/

// fmt package 는 표준입출력을 다루는 package이다.
import "fmt"

// Go언어로 만든 모든 프로그램은 main()함수로 시작되고, 해당 함수가 종료되면 프로그램이 종료된다.
func main() {
	// fmt package 에서 제공하는 문자열을 출력하는 함수이다.
	fmt.Println("Hello Go World")
}

 

 

setting : gopls

"experimentalWorkspaceModule": true,

study> go mod init chap3

 

4장 변수

 

데이타 형
+

 자료형	저장범위	 설명
 uint8	0 ~ 255	 부호 없는 8비트 정수형
 uint16	0 ~ 65,535	 부호 없는 16비트 정수형
 uint32	0 ~ 4,294,967,295	 부호 없는 32비트 정수형
 uint64	0 ~ 18,446,744,073,709,551,615	 부호 없는 64비트 정수형
 uint		 32비트 시스템에서는 uint32, 64비트 시스템에서는 uint64

 int8	-128 ~ 127	 부호 있는 8비트 정수형
 int16	-32,768 ~ 32,767	 부호 있는 16비트 정수형
 int32	-2,147,483,648 ~ 2,147,483,647	 부호 있는 32비트 정수형
 int64	-9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807	 부호 있는 64비트 정수형
 int		 32비트 시스템에서는 int32, 64비트 시스템에서는 int64

 float32		 IEEE-754 32비트 부동소수점, 7자리 정밀도
 float64		 IEEE-754 64비트 부동소수점, 12자리 정밀도
 complex64		 float32 크기의 실수부와 허수부로 구성된 복소수
 complex128		 float64 크기의 실수부와 허수부로 구성된 복소수
 uintptr		 uint와 같은 크기를 갖는 포인터형
 bool		 참, 거짓을 표현하기 위한 8비트 자료형
 byte		 8비트 자료형
 rune		 유니코드 저장을 위한 자료형, 크기는 int32와 동일
 string		 문자열을 저장하기 위한 자료형

 

 

  • 변수의 선언과 값변경
// ch4/ex4.1/ex4.1.go
package main

import "fmt"

func main() {
	var a int = 10                    // a 변수 선언      
	var msg string = "Hello Variable" // msg 변수 선언    

	a = 20               // a 변수 값 변경   
	msg = "Good Morning" // msg 변수 값 변경 
	fmt.Println(msg, a)  // msg 와 a 값 출력 
}

 

 

  • 아래 소스와 같이 변수를 선언할때  var를 사용하여 타입이 다른 변수 여러개를 한번에 선언할수있다.
  • b,c,d같이 전역변수를 var를 사용하여 선언할수있다. (b = c+d 는 에러가 나지 않는다.)
// ch4/ex4.1/ex4.1.go
package main

import "fmt"

var (
	b = c + d
	c = 10
	d = 30
)

func main() {
	var (
		a   int    = 10
		msg string = "Hello Variable"
	)
	a = 20               // a 변수 값 변경   - ❸
	msg = "Good Morning" // msg 변수 값 변경 - ❹
	fmt.Println(msg, a)  // msg 와 a 값 출력 - ❺

	fmt.Println(b, c, d)

}

 

 

 

  • 아래와 같이 변수를 선언할때 다른 변수값을 가져와서 초기화를 할수있다.
//ch4/ex4.2/ex4.2.go
package main

import "fmt"

func main() {
	var minimumWage int = 10 // ❶ 변수 minimumWage 선언 및 초기화
	var workingHour int = 20 // ❷ 변수 workingHour 선언 및 초기화

	// ❸ 변수 income 선언 및 초기화
	var income int = minimumWage * workingHour

	// 변수 minimumWage, workingHour, income 출력
	fmt.Println(minimumWage, workingHour, income)
}

 

 

  • 아래와 같이 다양한 방식으로 변수를 선언할수있다.
//ch4/ex4.3/ex4.3.go
package main

import "fmt"

func main() {
	var a int = 3 // 가장 기본 형태
	var b int     // 초깃값 생략 - 초깃값은 타입별 기본값으로 대체
	var c = 4     // 타입 생략 - 변수의 타입은 우변 값의 타입이 됩니다
	d := 5        // 선언대입문 := 을 사용해서 var 키워드와 타입 생략

	fmt.Println(a, b, c, d)
}

 

 

 

  • 연산이나 대입에서 타입이 다르면 에러가 발생한다.
  • 변수의 형변환, 
    • int() : int형으로 변환한다.
    • float64() : float64형으로 변환한다.
    • int64()  : int64형으로 변환한다.
//ch4/ex4.4/ex4.4.go
package main

import "fmt"

func main() {
	a := 3              // int
	var b float64 = 3.5 // float64

	var c int = int(b)  // ❶ float64에서 int로 변환
	d := float64(a * c) // int에서 float6로 변환

	var e int64 = 7
	f := int64(d) * e // float64에서 int64로 변환

	var g int = int(b * 3) // float64에서 int로 변환
	var h int = int(b) * 3 // float64에서 int로 변환 g와 값이 다릅니다.
	fmt.Println(g, h, f)
}

 

 

  • 아래의 소스에서 int16을 int8로 형변환했다. 이럴경우 상위 8비트는 제거된다. c는 127까지 저장되는데, 3456에서 상위8비트가 제거되어 저장되기때문에, -128값이 저장된다.
//ch4/ex4.5/ex4.5.go
package main

import "fmt"

func main() {
	var a int16 = 3456
	var c int8 = int8(a) // int16타입에서 int8타입으로 변환

	fmt.Println(a)
	fmt.Println(c) // int8타입인 c값 출력
}

 

 

  • 변수의 범위
    • 아래 소스에서 g는 전역변수 m,s는 지역변수(로컬변수)이다. 
    • 지역변수는 해당 범위를 벗어나면 제거된다.
//ch4/ex4.6/ex4.6.go
package main

import "fmt"

var g int = 10 // 패키지 전역 변수 선언 

func main() {
	var m int = 20 // 로컬 변수 선언 

	{
		var s int = 50 // 로컬 변수 선언 
		fmt.Println(m, s, g)
	} // s 로컬변수는 사라짐 

	m = s + 20 // Error 
} // main함수 끝

 

 

  • 실수의 표현
    • 실수형은 오차가 있기때문에, 아래 소스처럼 실수끼리 계산할경우에 약간의 오차가 발생할수있다. 
    • 정확한 수치연산이 필요하는 경우에는 실수타입사용에 주의해야한다. 
//ch4/ex4.7/ex4.7.go
package main

import "fmt"

func main() {
	var a float32 = 1234.523
	var b float32 = 3456.123
	var c float32 = a * b
	var d float32 = c * 3

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

 

 

 

5장 fmt패키지를 이용한 텍스트 입출력

 

  • fmt package는 Go언어에서 가장 기본적인 표준 라이브러리 
Print() 함수입력값을 출력한다.
Println() 함수입력값을 출력하고, 개행한다.
Printf() 서식(format)에 맞도록 입력값들을 출력한다.
//ch5/ex5.1/ex5.1.go
package main

import "fmt"

func main() {
	var a int = 10
	var b int = 20
	var f float64 = 32799438743.8297

	fmt.Print("a:", a, "b:", b)               // ❶
	fmt.Println("a:", a, "b:", b, "f:", f)    // ❷
	fmt.Printf("a: %d b: %d f:%f\n", a, b, f) // ❸
}

 

 

  • 아래는 Printf()함수를 사용해서, 서식을 지정하여 출력하는 예제이다. 
//ch5/ex5.2/ex5.2.go
package main

import "fmt"

func main() {
	var a = 123
	var b = 456
	var c = 123456789

	// 최소너비(5)보다 작은 경우에는 해당 서식에 맞추어서 출력된다.
	fmt.Printf("%5d, %5d\n", a, b)    // ❶ 최소 너비보다 짧은 값 너비 지정
	fmt.Printf("%05d, %05d\n", a, b)  // ❷ 최소 너비보다 짧은 값 0 채우기
	fmt.Printf("%-5d, %-05d\n", a, b) // ❸ 최소 너비보다 짧은 값 왼쪽 정렬

	// 최소너비보다 더 큰값을 출력하면 최소너비가 무시되어 출력된다.
	fmt.Printf("%5d, %5d\n", c, c)    // ➍ 최소 너비보다 긴 값 너비 지정
	fmt.Printf("%05d, %05d\n", c, c)  // ➎ 최소 너비보다 긴 값 0 채우기
	fmt.Printf("%-5d, %-05d\n", c, c) // ➏ 최소 너비보다 긴 값 왼쪽 정렬
}

 

 

  • Printf()함수를 사용해서, 서식을 지정하여 출력할때 실수는 소숫점 이하의 자릿수도 지정할수있다.
//ch5/ex5.3/ex5.3.go
package main

import "fmt"

func main() {
	var a = 324.13455
	var b = 324.13455
	var c = 3.14

	fmt.Printf("%08.2f\n", a) // ❶ 최소너비:8 소수점이하:2자리 0을 채움.
	fmt.Printf("%08.2g\n", b) // ❷ 최소너비:8 총숫자: 2자리 0을 채움
	fmt.Printf("%8.5g\n", b)  // ❸ 최소너비:8 총숫자: 5자리
	fmt.Printf("%f\n", c)     // ❹ 소수점이하 6자리까지 출력
}

 

 

  • 줄바꿈이나 탭이나 기타 특수문자를 아래와 같이 표현하여 출력할수있다. 
//ch5/ex5.4/ex5.4.go
package main

import "fmt"

func main() {
	str := "Hello\tGo\t\tWorld\n\"Go\"is Awesome!\n" // ❶ 문자열

	fmt.Print(str)        // ❷ 문자열을 기본 서식으로 출력
	fmt.Printf("%s", str) // ❸ 문자열을 %s 서식으로 출력
	fmt.Printf("%q", str) // ➍ 문자열을 %q 서식으로 출력, %q는 특수문자기능을 동작하지않고, 그대로 출력한다.
}

 

 

 

  • 표준입력 함수
Scan() 표준입력에서 값을 입력받는다.
Scanf() 표준입력에서 서식 형태로 값을 입력받는다.
Scanln() 표준입력에서 한줄을 입력해서 값을 입력받는다.

 

  • 아래는 정수값을 두개 입력받아 출력하는 예제이다. 
//ch5/ex5.5/ex5.5.go
package main

import "fmt"

func main() {
	var a int // ❶ 값을 저장할 변수
	var b int

	n, err := fmt.Scan(&a, &b) // ❷ 입력 두 개 받기
	if err != nil {            // ❸ 에러가 발생하면 에러 코드 출력
		fmt.Println(n, err)
	} else { // ➍ 정상 입력되면 입력값 출력
		fmt.Println(n, a, b)
	}
}

 

 

  • 서식에 맞춘 입력을 받는다.
//ch5/ex5.6/ex5.6.go
package main

import "fmt"

func main() {
	var a int
	var b int

	n, err := fmt.Scanf("%d %d\n", &a, &b) // ❶ 입력 두 개 받기
	if err != nil {
		fmt.Println(n, err)
	} else {
		fmt.Println(n, a, b)
	}
}

 

 

  • 한줄을 입력받아 인수로 들어온 변수 메모리 주소에 값을 채워준다.
//ch5/ex5.7/ex5.7.go
package main

import "fmt"

func main() {
	var a int
	var b int

	n, err := fmt.Scanln(&a, &b) // ❶ 값을 입력받습니다.
	if err != nil {              // 에러 발생 시
		fmt.Println(err) // 에러를 출력합니다.
	} else {
		fmt.Println(n, a, b)
	}
}

 

 

  • 사용자가 표준입력장치(Keyboard 같은..)로 입력하면 입력데이터는 표준 입출력스트림이라는 메모리 공간에 임시저장되고 Scan() 함수들은 그 표준입력된 스트림에서 값을 읽어 입력값을 처리한다. (FIFO구조)
//ch5/ex5.8/ex5.8.go
package main

import (
	"bufio" // ❶ io를 담당하는 패키지
	"fmt"
	"os" // 표준 입출력 등을 가지고 있는 패키지
)

func main() {
	stdin := bufio.NewReader(os.Stdin) // ❷ 표준 입력을 읽는 객체

	var a int
	var b int

	n, err := fmt.Scanln(&a, &b)
	if err != nil { // 에러 발생 시
		fmt.Println(err)       // 에러 출력
		stdin.ReadString('\n') // ❸ 표준 입력 스트림 지우기
	} else {
		fmt.Println(n, a, b)
	}
	n, err = fmt.Scanln(&a, &b) // ❹ 다시 입력받기
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println(n, a, b)
	}
}

 

 

나중에 반복문으로 처리할것 

 

 

 

 

 

6장 연산자

 

  • 연산자는 같은 타입만 연산이 가능하다. (정수와 실수를 서로 연산할수없음)
//ch6/ex6.1/ex6.1.go
package main

import "fmt"

func main() {
	var x int32 = 7 // ❶
	var y int32 = 3 // ❷

	var s float32 = 3.14 // ❸
	var t float32 = 5

	fmt.Println("x + y = ", x+y)
	fmt.Println("x - y = ", x-y)
	fmt.Println("x * y = ", x*y)
	fmt.Println("x / y = ", x/y) // 정수연산의 결과는 정수가 출력된다.
	fmt.Println("x % y = ", x%y) // ➎ ➍

	fmt.Println("s * t = ", s*t)
	fmt.Println("s / t = ", s/t) // 실수연산의 결과는 실수가 출력된다.
}

 

 

  • 비트연산자의 예제이다.
//ch6/ex6.2/ex6.2.go
package main

import "fmt"

func main() {
	var x1 int8 = 34   // ❶ 8비트 정수, 00100010
	var x2 int16 = 34  // ❷ 16비트 정수, 00000000 00100010
	var x3 uint8 = 34  // ❸ 8비트 부호가 없는 정수, 00100010
	var x4 uint16 = 34 // ➍ 16비트 부호가 없는 정수,00000000 00100010

	fmt.Printf("^%d = %5d,\t %08b\n", x1, ^x1, uint8(^x1)) // ➎
	fmt.Printf("^%d = %5d,\t %016b\n", x2, ^x2, uint16(^x2))
	fmt.Printf("^%d = %5d,\t %08b\n", x3, ^x3, ^x3)
	fmt.Printf("^%d = %5d,\t %016b\n", x4, ^x4, ^x4)
}

 

 

  • 시프트 연산자의 예제이다. 비트연산자를 사용하여 새롭게 채워지는 비트는 0으로 채워진다.
//ch6/ex6.3/ex6.3.go
package main

import "fmt"

func main() {
	var x int8 = 4  // ❶ 8비트 정수
	var y int8 = 64 // ❷ 8비트 정수

	fmt.Printf("x:%08b x<<2: %08b x<<2: %d\n", x, x<<2, x<<2) // ❸ 왼쪽 시프트
	fmt.Printf("y:%08b y<<2: %08b y<<2: %d\n", y, y<<2, y<<2) // ➍ 왼쪽 시프트
}

 

 

 

 

  • 아래 예제는 양수,음수에 따른 오른쪽시프트의 값이 어떻게 변하는지 보여주는 예제이다.
  • 부호있는 정수에서 음수값을 오른쪽시프트하면 1로 채워진다.
  • 부호없는 정수에서 오른쪽비트가 1,0과 상관없이 0으로 채워진다.
//ch6/ex6.4/ex6.4.go
package main

import "fmt"

func main() {
	var x int8 = 16   // ❶ 부호가 있는 정수, 부호 비트값이 0인 수
	var y int8 = -128 // ❷ 부호가 있는 정수, 부호 비트값이 1인 수
	var z int8 = -1   // ❸ 모든 비트값이 1인 정수
	var w uint8 = 128 // ➍ 부호 없는 정수, 최상위 비트값이 1인 양수

	fmt.Printf("x:%08b x>>2: %08b x>>2: %d\n", x, x>>2, x>>2)               // ➎ 오른쪽 시프트❶
	fmt.Printf("y:%08b y>>2: %08b y>>2: %d\n", uint8(y), uint8(y>>2), y>>2) // ➏ 오른쪽 시프트❷
	fmt.Printf("z:%08b z>>2: %08b z>>2: %d\n", uint8(z), uint8(z>>2), z>>2) // ➐ 오른쪽 시프트❸
	fmt.Printf("w:%08b w>>2: %08b w>>2: %d\n", uint8(w), uint8(w>>2), w>>2) // ➑ 오른쪽 시프트❹
}

 

 

  • 아래 예제는 오버플로우 , 언더플로우현상으로 값이 비정상적으로 변화하는 경우이다.
//ch6/ex6.5/ex6.5.go
package main

import "fmt"

func main() {
	var x int8 = 127 // ❶ 8비트 부호가 있는 정수 최댓값

	fmt.Printf("%d < %d + 1: %v\n", x, x, x < x+1) // ❷ 비교 연산 수행
	fmt.Printf("x\t= %4d, %08b\n", x, x)           // ❸ x값 확인
	fmt.Printf("x + 1\t= %4d, %08b\n", x+1, x+1)   // ➍ x + 1값 확인
	fmt.Printf("x + 2\t= %4d, %08b\n", x+2, x+2)   // ➎ x + 2값 확인
	fmt.Printf("x + 3\t= %4d, %08b\n", x+3, x+3)   // ➏ x + 3값 확인

	var y int8 = -128                              // 8비트 부호있는 정수 최솟값
	fmt.Printf("%d > %d - 1: %v\n", y, y, y > y-1) // ➐ 비교 연산 수행
	fmt.Printf("y\t= %4d, %08b\n", y, y)           // ➑ y값 확인
	fmt.Printf("y - 1\t= %4d, %08b\n", y-1, y-1)   // ➒ y - 1값 확인
}

 

 

 

  • 실수값은 오차때문에 == 연산자가 제대로 작동하지 않을수도있다. 아래의 예제처럼 0.1+0.2 와 0.3이 서로 같지 않을수있다.
//ch6/ex6.6/ex6.6.go
package main

import "fmt"

func main() {
	var a float64 = 0.1
	var b float64 = 0.2
	var c float64 = 0.3

	fmt.Printf("%f + %f == %f : %v\n", a, b, c, a+b == c) // ❶
	fmt.Println(a + b)                                    // ❷
}

 

 

  • 위의 문제를 해결하기 위하여 epsilon이라는 매우 작은 값을 사용하여, 두값을 비교할때 작은 차이를 무시하게 하여 두 값을 비교할수있다.
//ch6/ex6.7/ex6.7.go
package main

import "fmt"

const epsilon = 0.000001 // ❶ 매우 작은 값

func equal(a, b float64) bool {
	if a > b {
		if a-b <= epsilon { // ❷ 작은 차이는 무시합니다.
			return true
		} else {
			return false
		}
	} else {
		if b-a <= epsilon {
			return true
		} else {
			return false
		}
	}
}

func main() {
	var a float64 = 0.1
	var b float64 = 0.2
	var c float64 = 0.3

	fmt.Printf("%0.18f + %0.18f = %0.18f\n", a, b, a+b)          // ❸
	fmt.Printf("%0.18f == %0.18f : %v\n", c, a+b, equal(a+b, c)) // ❹

	a = 0.0000000000004 // ➎ 매우 작은 값으로 변경
	b = 0.0000000000002
	c = 0.0000000000007

	fmt.Printf("%g == %g : %v\n", c, a+b, equal(a+b, c))
}

 

 

  • 오차를 없애는 더 나은 방법 : math package의 Nextafter() 함수를 사용하여 두 값의 같음을 비교할수있다.
//ch6/ex6.8/ex6.8.go
package main

import (
	"fmt"
	"math"
)

func equal(a, b float64) bool {
	return math.Nextafter(a, b) == b // ❶ Nextafter() 로 값을 비교합니다.
}

func main() {
	var a float64 = 0.1
	var b float64 = 0.2
	var c float64 = 0.3

	fmt.Printf("%0.18f + %0.18f = %0.18f\n", a, b, a+b)
	fmt.Printf("%0.18f == %0.18f : %v\n", c, a+b, equal(a+b, c)) // ❷

	a = 0.0000000000004 // ❸ 매우 작은 값으로 변경
	b = 0.0000000000002
	c = 0.0000000000007

	fmt.Printf("%g == %g : %v\n", c, a+b, equal(a+b, c))
}

 

 

  • math package의 big의 Float를 사용하여 정밀도를 조정할수있다.
//ch6/ex6.9/ex6.9.go
package main

import (
	"fmt"
	"math/big"
)

func main() {
	a, _ := new(big.Float).SetString("0.1") // ❶
	b, _ := new(big.Float).SetString("0.2")
	c, _ := new(big.Float).SetString("0.3")

	d := new(big.Float).Add(a, b) // ❷
	fmt.Println(a, b, c, d)
	fmt.Println(c.Cmp(d)) // c가 작으면 -1, d가 작으면 1, 같으면 0을 출력한다.
}

 

 

  • Go에서 대입연산자를 사용할때 아래와같이 복수개의 값을 대입할수있다.
//ch6/ex6.10/ex6.10.go
package main

import "fmt"

func main() {
	var a int = 10
	var b int = 20

	a, b = b, a // ❶ a와 b값을 서로 바꿉니다.

	fmt.Println(a, b)
}

 

 

 

  • 연산자 우선순위
//ch6/ex6.11/ex6.11.go
package main

import "fmt"

func main() {
	fmt.Println(3*4^7<<2+3*5 == 7) // ❶
}

 

 

 

 

Function

 

 

7장 함수

 

 

 

  • 함수의 기본사용법은 아래와 같다. 
  • 함수의 이름, 매개변수, 반환타입을 지정하여 함수를 구현한다. 
  • 아래의 Add()함수는 두 정수값을 입력받아 그 합을 리턴하는 함수이다. 
//ch7/ex7.1/ex7.1.go
package main

import "fmt"

func Add(a int, b int) int { // ❶
	return a + b // ❷
}

func main() {
	c := Add(3, 6) // ❸
	fmt.Println(c) // ➍
}

 

 

  • 아래와 같이 각각의 점수를 출력하는 예제가 있는데, 3명이 아니라 100명, 1000명이라면 아주 비효율적인 코드가 될것이다. 
//ch7/ex7.2/ex7.2.go
package main

import "fmt"

func main() {
	math := 80
	eng := 74
	history := 95                                             // ❶
	fmt.Println("김일등 님 평균 점수는", (math+eng+history)/3, "입니다.") // ❷

	math = 88
	eng = 92
	history = 53

	fmt.Println("송이등 님 평균 점수는", (math+eng+history)/3, "입니다.")

	math = 78
	eng = 73
	history = 78

	fmt.Println("박삼등 님 평균 점수는", (math+eng+history)/3, "입니다.")
}

 

 

  • 위의 소스를 아래처럼 printAvgScore()  함수를 사용하여 중복코드를 없애 효율적으로 작성할수있다. 
//ch7/ex7.3/ex7.3.go
package main

import "fmt"

func printAvgScore(name string, math int, eng int, history int) { // ❶
	total := math + eng + history
	avg := total / 3
	fmt.Println(name, "님 평균 점수는", avg, "입니다.")
}

func main() {
	printAvgScore("김일등", 80, 74, 95) // ❷
	printAvgScore("송이등", 88, 92, 53)
	printAvgScore("박삼등", 78, 73, 78)
}

 

  • 위의 소스를 아래와 같이 함수의 매개변수를 가변인자를 사용할수있다.
  • scores 에 3개의 숫자가 입력되면 for문으로 한개씩 더하여 total을 구하고, 해당 total에 가변인자의 수만큼 나누어 평균을 구한다.
package main

import "fmt"

func printAvgScore(name string, scores ...int) { 
	total := 0
	for i := 0; i < len(scores); i++ {
		total += scores[i]
	}
	avg := total / len(scores)
	fmt.Println(name, "님 평균 점수는", avg, "입니다.")
}

func main() {
	printAvgScore("김일등", 80, 74, 95) 
	printAvgScore("송이등", 88, 92, 53)
	printAvgScore("박삼등", 78, 73, 78)
}

 

 

defer

  • defer 키워드는 함수가 종료되기전까지 특정구문을 지연시켰다가, 종료되기 직전에 해당 구문을 수행한다.
  • 아래의 함수를 실행해보면 f2() 나중에 실행된다.
package main

import "fmt"

func f() {
	fmt.Println("f() called")
	defer f2()
	fmt.Println("f() end")
}

func f2() {
	fmt.Println("f2() called")
}

func main() {
	f()
}

 

 

  • 아래처럼 defer를 여러번 실행하면 맨나중에 실행한 defer가 먼저 실행된다. FILO
package main

import "fmt"

func f() {
	for i := 0; i < 5; i++ {
		defer fmt.Printf("%d ", i)
	}
}

func main() {
	f()
}

 

 

클로저 : 클로저는 선언될때 해당 함수를 캡쳐하고, 호출할때 캡쳐한 값을 사용하는 익명함수이다.

  • 아래는 클로저의 기본예제이다.
package main

import "fmt"

func main() {
	sum := func(a, b int) int {
		return a + b
	}

	c := sum(1, 2)
	fmt.Println(c)
}

 

  • 아래의 소스에서는 nextValue()가 익명함수(클로저)를 리턴한다. 해당 함수에서 i는 함수밖에 있지만, 해당 함수가 호출할때 해당값을 캡쳐하기때문에, 해당 변수를 계속 사용가능하다. 
package main

func nextValue() func() int {
	i := 0
	return func() int {
		i++
		return i
	}
}

func main() {
	next := nextValue()
	println(next())
	println(next())
	println(next())
	next2 := nextValue()
	println(next2())
	println(next2())

}

 

 

 

  • 다른 언어와 달리 Go언어는 여러값을 반환할수있다. 
  • 아래의 예제처럼 int, bool을 동시에 반환가능하다.
//ch7/ex7.4/ex7.4.go
package main

import "fmt"

func Divide(a, b int) (int, bool) { // ❶ 함수 선언
	if b == 0 {
		return 0, false // ❷ 제수가 0일 때 반환
	}
	return a / b, true // ❸ 제수가 0이 아닐 때 반환
}

func main() {
	c, success := Divide(9, 3) // ➍ 제수가 0이 아닌 경우
	fmt.Println(c, success)
	d, success := Divide(9, 0) // ➎ 제수가 0인 경우
	fmt.Println(d, success)
}

 

  • 함수를 반환할때 아래와 같이 반환한 변수명을 명시할수있다. 
//ch7/ex7.5/ex7.5.go
package main

import "fmt"

func Divide(a, b int) (result int, success bool) { // ❶ 반환할 변수명이 명시된 함수 선언부
	if b == 0 {
		result = 0
		success = false
		return // ❷ 명시적으로 반환할 값을 지정하지 않은 return문
	}
	result = a / b
	success = true
	return
}

func main() {
	c, success := Divide(9, 3)
	fmt.Println(c, success)
	d, success := Divide(9, 0)
	fmt.Println(d, success)
}

 

 

  • 아래소스와 같이 재귀호출을 할수도있다. 

 

//ch7/ex7.6/ex7.6.go
package main

import "fmt"

func printNo(n int) {
	if n == 0 { // ❶ 재귀 호출 탈출 조건
		return
	}
	fmt.Println(n)
	printNo(n - 1)          // ❷ 재귀 호출
	fmt.Println("After", n) // ❸ 재귀 호출 이후 출력
}

func main() {
	printNo(3) // ❹ 함수 호출
}

 

  • 아래는 피보나치 수열을 구현하는 재귀호출 함수 예제이다.
package main

import "fmt"

func F(n int) int {
	if n < 2 {
		return 1
	}
	return F(n-2) + F(n-1)
}

func main() {
	fmt.Println(F(9))
}