A Tour of Go 4

태그: ,

카테고리: ,

출처 Go Tour

🚫 아래 내용은 주관적인 생각이므로 사실과 다를 수 있습니다.



Range 1

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)
	}
}
// 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는 슬라이스 또는 맵을 순회한다

슬라이스를 대상으로 range를 사용할 때, 매 순회마다 두 값이 반환되는데,
첫번째 값은 인덱스이고, 두 번째 값은 해당 인덱스의 요소의 복사본이다


Range 2

package main

import "fmt"

func main() {
	pow := make([]int, 10)
	for i := range pow {
		pow[i] = 1 << uint(i) // == 2**i
	}
	for _, value := range pow {
		fmt.Printf("%d\n", value)
	}
}
// 1
// 2
// 4
// 8
// 16
// 32
// 64
// 128
// 256
// 512

_를 할당해서 인덱스 또는 값을 생략할 수 있다

for i, _ := range pow
for _, value := range pow

인덱스만 사용할거라면 두 번째 변수를 생략해도 된다

for i := range pow

Exercise: Slices

package main

import "golang.org/x/tour/pic"

func Pic(dx, dy int) [][]uint8 {
	result := make([][]uint8, dy)
	for i := 0; i < dy; i++ {
		result[i] = make([]uint8, dx)
	}
	
	for y := 0; y < dy; y++ {
		for x := 0; x < dx; x++ {
			switch (x * y) % 3 {
				case 0:
					result[y][x] = uint8((x + y) / 2)
				case 1:
					result[y][x] = uint8(x * y)
				case 2:
					result[y][x] = uint8(x ^ y)
			}
		}
	}
	
	return result
}

Pic 함수를 만들어보자
가로 세로 각각 입력받은 dx, dy길이의
uint8 타입 2차원 배열을 반환하면 된다

각 요소의 값들을 다르게 지정해 출력되는 이미지를 바꿀 수 있다
(x+y)/2, x*y, x^y 등의 수식을 사용하는것도 좋다

  • 반복문을 통해 2차원 배열([][]uint8)의 요소들에 각각의 배열([]uint8)들을 할당해줘야 한다
  • uint8(정수값)을 사용해 형변환한다

Maps

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"])
}
// {40.68433 -74.39967}

맵은 키에 값을 매핑한다
맵의 기본값은 nil이고, nil 맵은 키도 없고 키를 추가할 수도 없다

make 함수는 입력된 타입의 맵(초기화되고 바로 사용가능한)을 반환한다


Map literals 1

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)
}
// map[Bell Labs:{40.68433 -74.39967} Google:{37.42202 -122.08408}]

맵 리터럴은 기본적으로 구조체 리터럴과 비슷하지만 키가 필요하다


Map literals 2

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)
}
// map[Bell Labs:{40.68433 -74.39967} Google:{37.42202 -122.08408}]

리터럴 요소의 타입이 최상위의 타입과 동일하다면, 타입을 생략해도 된다


Mutating Maps

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)
}
// The value: 42
// The value: 48
// The value: 0
// The value: 0 Present? false

맵 m의 요소를 업데이트 또는 삽입

m[key] = elem

요소 가져오기

elem = m[key]

요소 삭제

delete(m, key)

두 개의 값을 할당해 맵에 키가 있는지 확인

elem, ok = m[key]

맵 m에 키가 있으면 ok값은 true, 없으면 false다
맵에 키가 없으면 elem의 값은 요소 타입의 기본값이 된다

elem, ok 모두 초기화된 적이 없다면 아래와 같이 할당이 가능하다

elem, ok := m[key]

Exercise: Maps

package main

import (
	"golang.org/x/tour/wc"
	"strings"
)

func WordCount(s string) map[string]int {
	result := make(map[string]int)
	for _, v := range strings.Fields(s) {
		result[v] = result[v] + 1
	}
	return result
}

func main() {
	wc.Test(WordCount)
}
// PASS
//  f("I am learning Go!") = 
//   map[string]int{"Go!":1, "I":1, "am":1, "learning":1}
// PASS
//  f("The quick brown fox jumped over the lazy dog.") = 
//   map[string]int{"The":1, "brown":1, "dog.":1, "fox":1, "jumped":1, "lazy":1, "over":1, "quick":1, "the":1}
// PASS
//  f("I ate a donut. Then I ate another donut.") = 
//   map[string]int{"I":2, "Then":1, "a":1, "another":1, "ate":2, "donut.":2}
// PASS
//  f("A man a plan a canal panama.") = 
//   map[string]int{"A":1, "a":2, "canal":1, "man":1, "panama.":1, "plan":1}

WordCount 함수를 만들어보자

  • 입력값 s string을 공백 문자 기준으로 분할해
  • 단어별 출현 횟수를 맵에 담아 반환하면 된다

strings.Fields 문서가 도움이 될 것이다


Function values

package main

import (
	"fmt"
	"math"
)

func compute(fn func(float64, float64) float64) float64 {
	return fn(3, 4)
}

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

	fmt.Println(compute(hypot))
	fmt.Println(compute(math.Pow))
}
// 13
// 5
// 81

함수도 값이다
함수도 다른 변수들처럼 전달할 수 있다

함수값은 파라미터나 반환값으로 사용될 수 있다


Function closures

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),
		)
	}
}
// 0 0
// 1 -2
// 3 -6
// 6 -12
// 10 -20
// 15 -30
// 21 -42
// 28 -56
// 36 -72
// 45 -90

클로저(closure)는 내부함수와 밀접한 관계를 가지고 있는 주제다.
내부함수는 외부함수의 지역변수에 접근 할 수 있는데
외부함수의 실행이 끝나서 외부함수가 소멸된 이후에도
내부함수가 외부함수의 변수에 접근 할 수 있다.
이러한 메커니즘을 클로저라고 한다.

Go에서도 클로저를 사용할 수 있다
클로저에서 내부함수는 외부함수의 변수에 “bound(바운드)” 된다
예를 들면, 위 코드의 adder 함수는 클로저를 반환하고,
각 클로저는 그 자체의 sum 변수에 bound(바운드) 된다


Exercise: Fibonacci closure

package main

import "fmt"

// fibonacci is a function that returns
// a function that returns an int.
func fibonacci() func() int {
	prev := 0
	next := 1
	return func() int {
		temp := prev
		prev = next
		next = temp + prev
		return temp
	}
}

func main() {
	f := fibonacci()
	for i := 0; i < 10; i++ {
		fmt.Println(f())
	}
}
// 0
// 1
// 1
// 2
// 3
// 5
// 8
// 13
// 21
// 34

클로저로 피보나치 수를 구현해보자


댓글남기기