Returning Multiple Values
다수의 값을 함수를 통해 반환할 수 있다.
1 2 3 4 5 6 7 | func f() (int, int) { return 5, 6 } func main() { x, y := f() } | cs |
이를 위해서 3가지 변경이 필요하다. 먼저, 반환형을 변경해야 하고( ,, 로 구분). 다음으로 표현식을 변경해야한다(, 로 구분). 마지막으로 := or = 의 좌측에 다수의 값이 존재할 수 있도록 대입문을 변경해야 한다.
주로 결과와 에러값을 같이 포함해서 반환하거나 또는 성공 여부를 가리는 불린값을 같이 반환할 때 주로 쓰인다.
Variadic Functions
Go 함수에는 특수한 형식의 함수가 존재하는데, 가변함수가 그것이다. 다음의 예를 보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | package main import "fmt" func add(args ...int) int { total := 0 for _, v := range args { total += v } return total } func main() { fmt.Println(add(1,2,3)) } | cs |
1 2 | ~/go/src/golang-book/chapter7# go run main.go 6 | cs |
마지막 파라메터 타입명 앞에 ... 를 넣음으로써, 이러한 타입의 파라메터가 다수 존재함을 알려줄 수 있다. 이 예제에서는 다수의 int 가 존재하는 것이다. 이를 통해 원하는 만큼이 int 를 전달할 수 있다.
이것은 fmt.Println 함수가 구현되는 방식과 동일하다. Println 함수이 어떤 타입이든 다수의 값을 가질 수 있다.
1 | func Println(a ...interface{}) (n int, err error) | cs |
그리고 ints 의 일부분만 ... 을 통해서 전달할 수도 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | package main import "fmt" func add(args ...int) int { total := 0 for _, v := range args { total += v } return total } func main() { xs := []int{1,2,3} fmt.Println(add(xs...)) } | cs |
Closure
함수 안에 함수를 생성하는 것도 가능하다.
1 2 3 4 5 6 | func main() { add := func(x, y int) int { return x + y } fmt.Println(add(1,1)) } | cs |
여기서 add 는 func(int, int) int (2개의 int 를 받아 1개의 int 를 반환해주는 함수) 타입을 가진 지역 변수이다. 이런 지역 함수를 생성할 때, 다른 지역변수에 대한 접근도 가능하다.
1 2 3 4 5 6 7 8 9 10 11 12 13 | package main import "fmt" func main() { x := 0 increment := func() int { x++ return x } fmt.Println(increment()) fmt.Println(increment()) } | cs |
increment 는 main 함수 단에서 정의된 x 에 1을 더한다. 이 x 변수는 increment 함수에 의해 억세스되고 변경될 수 있다. 이것이 첫 줄에는 1이 다음에는 2가 출력되는 이유다.
위와 같이 함수와 그것이 참조하는 비국부변수를 묶어서 closure 라고 한다. 여기서는 increment 와 변수 x 가 closure 를 형성한다.
closure 를 사용하는 하나의 방법은 다른 함수를 반환하는 함수를 작성하는 것이다. 아래는 모든 짝수를 생성하는 예제이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | package main import "fmt" func makeEvenGenerator() func() uint { i := uint(0) return func() (ret uint) { ret = i i += 2 return } } func main() { nextEven := makeEvenGenerator() fmt.Println(nextEven()) // 0 fmt.Println(nextEven()) // 2 fmt.Println(nextEven()) // 4 } | cs |
makeEvenGenerator 는 짝수를 생성하는 함수를 반환한다. 매번 호출될 때마다 지역 변수 i 에 2를 더하게 된다.
Recursion
마지막으로 살펴볼 함수는 스스로를 호출하는 형태이다. factorial 을 계산하는 아래 함수를 보자.
1 2 3 4 5 6 | func factorial(x uint) uint { if x == 0 { return 1 } return x * factorial(x-1) } | cs |
factorial 는 자신을 호출한다. 즉, 재귀적으로 만드는 것이다.
Closure 와 recursion 은 강력한 프로그래밍 테크닉으로, functional programming 의 기본 패러다임을 구성한다.
Defer, Panic & Recover
Go 는 함수가 완료된 후 함수 호출이 이루어지게 하는 defer 라는 특수한 명령문을 가지고 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | package main import "fmt" func first() { fmt.Println("1st") } func second() { fmt.Println("2nd") } func main() { defer second() first() } | cs |
실행결과는 다음과 같다. 1st, 2nd 를 차례로 보여준다.
1 2 3 4 | ~/go/src/golang-book/chapter7# go run main.go 1st 2nd | cs |
defer는 리소스가 자유롭게 활용되길 원할 때 종종 사용된다. defer 를 이용하여 파일 클로징을 추후에 확인할 수 있다.
1 2 | f, _ := os.Open(filename) defer f.Close() | cs |
이는 3가지 장점을 가진다.
(1) 종료 call 과 오픈 call 이 가까이에 있어 이해하기 쉽다.
(2) 만약 함수가 다수의 반환문을 가지는 경우 (예를 들어, 하나는 if 문에 다른 하나는 else 문에 위치) Close 는 그들 모두에 앞서 발생한다.
(3) 심지어 런타임 패닉이 발생해도 실행된다.
Panic & Recover
앞서 런타임 에러를 발생시키는 panic function 을 호출하는 함수를 이미 만든바 있다. 내장된 recover 함수를 이용해 런타임 패닉을 다룰 수 있는데, recover 는 panic을 멈추고 전달된 값을 반환한다. 다음과 같이 작성할 수 있다.
1 2 3 4 5 6 7 8 9 | package main import "fmt" func main() { panic("PANIC") str := recover() fmt.Println(str) } | cs |
1 2 3 4 5 6 7 8 | ~/go/src/golang-book/chapter7# go run main.go panic: PANIC goroutine 1 [running]: main.main() /root/go/src/golang-book/chapter7/main.go:6 +0x39 exit status 2 | cs |
하지만 recover 하려는 호출은 절대 일어나지 않는데, 그 이유는 panic 이 즉각 함수의 실행을 중지시켰기 때문이다. 대신 defer 와 짝으로 구성해야 한다.
1 2 3 4 5 6 7 8 9 10 11 | package main import "fmt" func main() { defer func() { str := recover() fmt.Println(str) }() panic("PANIC") } | cs |
1 2 | ~/go/src/golang-book/chapter7# go run main.go PANIC | cs |
'프로그래밍 Programming' 카테고리의 다른 글
Go 웹어플리케이션 만들기 Building a Go Web App from Scratch (0) | 2018.10.13 |
---|---|
SQLTools connect - cannot save "connections.xml" error (0) | 2018.09.17 |
Go 언어 입문 - Control Structures - Functions (1) (golang-book) (0) | 2018.09.09 |
Go 언어 입문 - Control Structures - Maps (golang-book) (0) | 2018.09.05 |
Go 언어 입문 - Control Structures - Slice (golang-book) (0) | 2018.09.04 |