A tour of Go (2)


본 카테고리는 구글에서 만든 언어인 Go를 다룬다.

좀 더 자세한 내용은 아래의 공식 사이트를 참고하자.

참고 Go Language 공식 사이트

30. 슬라이스(Slices)

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import "fmt"

func main() {
p := []int{2, 3, 5, 7, 11, 13}
fmt.Println("p ==", p)

for i := 0; i < len(p); i++ { // len(p)는 p의 길이를 반환한다.
fmt.Printf("p[%d] == %d\n",
i, p[i])
}
}
1
2
3
4
5
6
7
8
# 실행 결과
p == [2 3 5 7 11 13]
p[0] == 2
p[1] == 3
p[2] == 5
p[3] == 7
p[4] == 11
p[5] == 13

슬라이스는 배열의 값을 가리키는 포인터이다.

추가적으로 슬라이드는 배열의 길이 또한 가지고 있다.

[]T 형태로 표현하며, 이는 T타입의 요소를 가진 슬라이스를 뜻한다.

31. 슬라이스 자르기 (Slicing slices)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import "fmt"

func main() {
p := []int{2, 3, 5, 7, 11, 13}
fmt.Println("p ==", p)
fmt.Println("p[1:4] ==", p[1:4]) // low ~ high까지의 인덱스 요소만 출력

// missing low index implies 0
fmt.Println("p[:3] ==", p[:3]) // default : 0

// missing high index implies len(s)
fmt.Println("p[4:] ==", p[4:]) // defualt : len(SLICE_NAME)
}
1
2
3
4
5
# 실행결과
p == [2 3 5 7 11 13]
p[1:4] == [3 5 7]
p[:3] == [2 3 5]
p[4:] == [11 13]

슬라이스의 특정 구간을 분할하거나, 동일한 배열을 가리키는 포인터를 가진 새로운 슬라이스를 만들 수 있다.

위의 예제 외에도 SLICE_NAME[lo:lo] 형태는 empty slice를 반환하며

SLICE_NAME[lo:lo + 1]은 하나의 요소를 가진 slice를 뜻한다.

32. 슬라이스 만들기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

import "fmt"

func main() {
a := make([]int, 5)
printSlice("a", a)
b := make([]int, 0, 5)
printSlice("b", b)
c := b[:2]
printSlice("c", c)
d := c[2:5]
printSlice("d", d)
}

func printSlice(s string, x []int) {
fmt.Printf("%s len=%d cap=%d %v\n",
s, len(x), cap(x), x)
}
1
2
3
4
5
# 실행결과
a len=5 cap=5 [0 0 0 0 0]
b len=0 cap=5 []
c len=2 cap=5 [0 0]
d len=3 cap=3 [0 0 0]

슬라이스를 만들 땐 make 함수를 이용해 제작한다.

최초 생성된 슬라이스는 모든 요소가 0으로 할당된 배열을 참조하게 된다.

생성 문법은 아래와 같다.

make(ELEMENT_TYPE, LENGTH, CAPACITY)

세 번째 파라미터인 CAPACITY를 이용해 슬라이스의 크기를 제한할 수도 있다.

33. 빈 슬라이스

1
2
3
4
5
6
7
8
9
10
11
package main

import "fmt"

func main() {
var z []int
fmt.Println(z, len(z), cap(z))
if z == nil {
fmt.Println("nil!")
}
}
1
2
3
# 실행결과
[] 0 0
nil!

슬라이스의 zero value는 nil로 표현하며, 현재 길이와 최대 크기는 0으로 표현된다.

34. 레인지 (Range, 범위)

1
2
3
4
5
6
7
8
9
10
11
package main

import "fmt"

var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}

func main() {
for i, v := range pow {
fmt.Printf("2**%d = %d\n", i, v)
}
}
1
2
3
4
5
6
7
8
9
# 실행결과
2**0 = 1
2**1 = 2
2**2 = 4
2**3 = 8
2**4 = 16
2**5 = 32
2**6 = 64
2**7 = 128

for문에서 range를 이용해 슬라이스를 순회할 수 있다.

35. 레인지 (2)

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import "fmt"

func main() {
pow := make([]int, 10)
for i := range pow {
pow[i] = 1 << uint(i)
}
for _, value := range pow {
fmt.Printf("%d\n", value)
}
}
1
2
3
4
5
6
7
8
9
10
11
# 실행결과
1
2
4
8
16
32
64
128
256
512

for문에서 range를 사용하면서 _를 이용해 특정 인덱스나 값을 무시할 수 있다.

36. 연습: 슬라이스

http://go-tour-kr.appspot.com/#36

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import "code.google.com/p/go-tour/pic"

func Pic(dx, dy int) [][]uint8 {
var s = make([][]uint8, dy) // dy개 만큼의 길이를 가지는 슬라이스

for i := range s {
s[i] = make([]uint8, dx) // 각각의 요소들은 또한 dx 개의 8비트 부호없는 8비트 정수 타입을 가지는 슬라이스

for j := range s[i] {
s[i][j] = uint8(i^j) // x^y
}
}

return s // dy개 만큼의 길이를 가지는 슬라이스를 리턴해야 하는데
}

func main() {
pic.Show(Pic)
}

37. 맵 (Maps)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import "fmt"

type Vertex struct {
Lat, Long float64
}

var m map[string]Vertex

func main() {
m = make(map[string]Vertex)
m["Bell Labs"] = Vertex{
40.68433, -74.39967,
}
fmt.Println(m["Bell Labs"])
}
1
2
# 실행결과
{40.68433 -74.39967}

맵은 Key-Value 형태로 쓸 수 있는 자료구조이다.

맵은 반드시 사용하기 전에 make를 명시해야 하며, 명시하지 않으면 nil 형태로 되어 값을 저장할 수 없다.

38. 맵 리터럴 (Map literals)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

import "fmt"

type Vertex struct {
Lat, Long float64
}

var m = map[string]Vertex{
"Bell Labs": Vertex{
40.68433, -74.39967,
},
"Google": Vertex{
37.42202, -122.08408,
},
}

func main() {
fmt.Println(m)
}
1
2
# 실행결과
map[Bell Labs:{40.68433 -74.39967} Google:{37.42202 -122.08408}]

맵은 반드시 key를 지정해서 데이터를 저장해야 한다.

저장과 할당은 정확히 어떻게 다를까?

39. 맵 리터럴 (2)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import "fmt"

type Vertex struct {
Lat, Long float64
}

var m = map[string]Vertex{
"Bell Labs": {40.68433, -74.39967},
"Google": {37.42202, -122.08408},
}

func main() {
fmt.Println(m)
}
1
2
# 실행결과
map[Bell Labs:{40.68433 -74.39967} Google:{37.42202 -122.08408}]

최상위 유형의 타입이 타입명을 경우 생략할 수 있다.

40. 맵 다루기 (Mutating Maps)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

import "fmt"

func main() {
m := make(map[string]int)

m["Answer"] = 42
fmt.Println("The value:", m["Answer"])

m["Answer"] = 48
fmt.Println("The value:", m["Answer"])

delete(m, "Answer")
fmt.Println("The value:", m["Answer"])

v, ok := m["Answer"]
fmt.Println("The value:", v, "Present?", ok)
}
1
2
3
4
5
# 실행결과
The value: 42
The value: 48
The value: 0
The value: 0 Present? false

특정 key의 요소를 재할당하는 경우

1
m[key] = elem

특정 key의 요소값을 가져오는 경우

1
elem = m[key] //

특정 key의 요소값을 제거하는 경우

1
delete(m, key)

특정 key의 존재여부를 경우 (value, Bool)

1
elem, ok = m[key]

맵의 데이터를 읽을 때, 존재하지 않는 key로 조회하는 경우 각 요소의 타입에 맞는 zero value를 반환한다.

41. 연습: 맵

http://go-tour-kr.appspot.com/#41

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import (
"code.google.com/p/go-tour/wc"
"strings"
)

func WordCount(s string) map[string]int {

m := make(map[string]int)

for _, v := range strings.Fields(s) {
m[v]++ // 각각의 "단어"의 등장 횟수
}

return m
}

func main() {
wc.Test(WordCount)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
# 실행결과
PASS
f("I am learning Go!") =
map[string]int{"I":1, "am":1, "learning":1, "Go!":1}
PASS
f("The quick brown fox jumped over the lazy dog.") =
map[string]int{"jumped":1, "the":1, "lazy":1, "The":1, "quick":1, "over":1, "dog.":1, "brown":1, "fox":1}
PASS
f("I ate a donut. Then I ate another donut.") =
map[string]int{"donut.":2, "Then":1, "another":1, "I":2, "ate":2, "a":1}
PASS
f("A man a plan a canal panama.") =
map[string]int{"plan":1, "canal":1, "panama.":1, "A":1, "man":1, "a":2}
1
2
3
4
5
6
7
8
9
10
11
// strings.Fields Example
package main

import (
"fmt"
"strings"
)

func main() {
fmt.Printf("Fields are: %q", strings.Fields(" foo bar baz "))
}

42. 함수 값 (Function values)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import (
"fmt"
"math"
)

func main() {
hypot := func(x, y float64) float64 {
return math.Sqrt(x*x + y*y)
}

fmt.Println(hypot(3, 4))
fmt.Printf("%f", hypot(3, 4))
}
1
2
3
# 실행결과
5
5.000000

Go에서는 함수도 값으로 취급한다. 따라서 특정 파라미터처럼 사용할 수도 있다.

43. 함수 클로저 (Function closures)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import "fmt"

func adder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}

func main() {
pos, neg := adder(), adder()
for i := 0; i < 10; i++ {
fmt.Println(
pos(i),
neg(-2*i),
)
}
}
1
2
3
4
5
6
7
8
9
10
11
# 실행결과
0 0
1 -2
3 -6
6 -12
10 -20
15 -30
21 -42
28 -56
36 -72
45 -90

함수는 클로저이기도 하다.

위의 예제에서 adder 함수는 클로저를 반환하게 된다.

또한 각각의 클로저는 자신만의 변수 sum을 가지고 있다.

44. 연습: 피보나치 클로져

http://go-tour-kr.appspot.com/#44

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import "fmt"

// fibonacci is a function that returns
// a function that returns an int.
func fibonacci() func() int {
n1 := 0
n2 := 1

return func() int {
n := n1 + n2
n1 = n2
n2 = n
return n
}
}

func main() {
f := fibonacci()
for i := 0; i < 10; i++ {
fmt.Println(f())
}
}
1
2
3
4
5
6
7
8
9
10
11
# 실행결과
1
2
3
5
8
13
21
34
55
89

45. 스위치 (Switch)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

import (
"fmt"
"runtime"
)

func main() {
fmt.Print("Go runs on ")
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("OS X.")
case "linux":
fmt.Println("Linux.")
default:
// freebsd, openbsd,
// plan9, windows...
fmt.Printf("%s.", os)
}
}
1
2
# 실행결과
Go runs on nacl.

다른 일반적인 언어와는 다르게 Go의 스위치는 case body의 실행이 끝나면 자동으로 break를 한다.

단, fallthrough 키워드를 쓰면 break를 하지 않는다.

46. 스위치 동작 순서

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import (
"fmt"
"time"
)

func main() {
fmt.Println("When's Saturday?")
today := time.Now().Weekday()
switch time.Saturday {
case today + 0:
fmt.Println("Today.")
case today + 1:
fmt.Println("Tomorrow.")
case today + 2:
fmt.Println("In two days.")
default:
fmt.Println("Too far away.")
}
}
1
2
3
# 실행결과
When's Saturday? # 2018년 09월 18일 기준
In two days.

스위치의 condition이 true인 case를 찾으면 스위치문이 종료된다.

47. 조건을 생략한 스위치

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import (
"fmt"
"time"
)

func main() {
t := time.Now()
switch {
case t.Hour() < 12:
fmt.Println("Good morning!")
case t.Hour() < 17:
fmt.Println("Good afternoon.")
default:
fmt.Println("Good evening.")
}
}
1
2
# 실행결과
Good morning!

스위치에서 condition을 생략하면 true인 case를 찾는 switch true와 동일하게 처리된다.

48. 심화 연습: 복소수 세제곱근

http://go-tour-kr.appspot.com/#48

1
2
3
4
5
6
7
8
9
10
11
12
func Cbrt(x complex128) complex128 {
z := cmplx.Pow(x, 3)

for z != Newtons(z, x) {
z = z - (cmplx.Pow(z, 3) - x) / (3 * cmplx.Pow(z, 2))
}
return z
}

func Newtons(z, x complex128) complex128 {
return z - (cmplx.Pow(z, 3) - x) / (3 * cmplx.Pow(z, 2))
}
1
2
# 실행결과
(1.2599210498948732+0i)