Go 기초

Package

  • package main에서 시작

  • 대문자로 시작해야 exported.
    Import 시 exported name만 접근 가능.

    package main
      
    import (
    	"fmt"
    	"math"
    )
      
    func main() {
    	fmt.Println(math.Pi) // 3.141592653589793
    	fmt.Println(math.pi) // compile error(cannot refer to unexported name math.pi)
    }
    
    

Function

  • 타입이 변수 이름

    func add(x int, y int) int {
    	return x + y
    }
      
    // 위와 같음
    func add(x, y int) int { // 연속적인 경우 마지막에만 명시 가능
    	return x + y
    }
    
  • 여러 개 리턴 가능

    func swap(x, y string) (string, string) {
    	return y, x
    }	
    
  • naked return : 가독성 떨어지므로 짧은 함수에만 활용 권장

    func split(sum int) (x, y int) {
    	x = sum * 4 / 9
    	y = sum - x
    	return
    }
      
    // 같은 표현
    func split(sum int) (int, int){
    	x := sum * 4 / 9
    	y := sum - x
        return x, y
    }
    

Variable

  • 선언은 var 변수명 type

    func main(){
    	var i, j int = 1, 2
    	var c, python, java = true, false, "no!"
        // initializer와 함께 사용 시 type 생략 가능
    	k:= 3 // var k = 3과 같은 표현. 그러나 함수 안에서만 사용 가능
        	
        // 명시적으로 초기화 안했을 경우 "Zero" value로 초기화
        // Zero value = 0(숫자 타입), false(boolean 타입), ""(string 타입), nil(pointer 타입)
        var j int
    	var f float64
    	var b bool
    	var s string
    	fmt.Printf("%v %v %v %q\n", j, f, b, s) // 0 0 false ""
    }
      
    k:=3 // syntax error: non-declaration statement outside function body
      
    
  • Basic types

    bool
    string
    int  int8  int16  int32  int64
    uint uint8 uint16 uint32 uint64 uintptr 
    // int, uint, uintptr은 32-bit 시스템에서는 32 bit, 64-bit system에서는 64 bit
    byte // alias for uint8
    rune // alias for int32. represents a Unicode code point
    float32 float64
    complex64 complex128
    
  • Type casting : 반드시 명시적으로 표기

    var x, y int = 3, 4
    var f float64 = math.Sqrt(float64(x*x + y*y))
    var z uint = uint(f)
    var z uint = f // cannot use f (type float64) as type uint in assignment
      
    // 명시 안 한 경우는 right side와 같은 타입
    func main() {
    	a := 42
    	b := 42.1
    	c := 42+5i
    	fmt.Printf("Types are %T %T %T\n", a, b, c) // Types are int float64 complex128
    }
    
  • const

    const Pi = 3.14
    func main() {
    	const World = "世界"
    	// const 변수는 := 로 선언 불가능
    	const i := 1 // syntax error: unexpected :=, expecting =
    }
    

흐름제어

For

  • 유일한 반복문 (while 없음)

  • for문 () 없고 {} 있음

    package main
      
    import "fmt"
      
    func main() {
    	sum := 0
    	for i := 0; i < 10; i++ {
    		sum += i
    	}
        
    	sum := 1
    	for sum < 1000 { // ; 제거시 while처럼 사용 가능
    		sum += sum
    	}
        
    	for { // 무한 루프
    	}
    }
    

If

  • 마찬가지로 () 없고 {} 있음
func sqrt(x float64) string {
	if x < 0 {
		return sqrt(-x) + "i"
	}
	return fmt.Sprint(math.Sqrt(x))
}

func pow(x, n, lim float64) float64 {
  	// if문 내 scope를 갖는 변수 선언 및 할당 가능. ;으로 구분. 마지막에 조건문
	if v := math.Pow(x, n); v < lim {
		return v
	} else {
		fmt.Printf("%g >= %g\n", v, lim)
	}
  	// return v (undefined : v)
	return lim
}

Switch

  • break문 없음. 무조건 조건 여러 개 중 하나만 실행(다른 언어는 break 없으면 다 돈다)
    • 조건 맞는거 만나면 실행하고 switch문 빠져나옴
  • 정수일 필요 없음
  • case가 상수일 필요도 없음
  • 조건이 없어도 됨
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:
		fmt.Printf("%s.\n", os)
	}
  
  	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.")
	}
  
  	// 긴 if-else문의 연속을 switch로 깔끔하게 쓸 수 있다. 
  	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.")
	}
}

Defer

  • 자신이 속한 함수의 return 이후 실행되도록(java finally와 유사)
  • 함수 argument는 바로 넣지만 실행만 미루는 것
  • 이때 stack 방식으로 쌓아놓는다. LIFO
  • 활용 예 : 다른 함수, 모듈이 일을 다하고 마지막에 clean-up할때 활용 가능
  • 참고 https://blog.golang.org/defer-panic-and-recover
func main() {
	fmt.Println("counting")

	for i := 0; i < 10; i++ {
		defer fmt.Print(i)
	}

	fmt.Println("done")
}
/* 
실행 결과
counting
done
9876543210
*/

func c() (i int) {
    defer func() { i++ }()
    return 1
}
// c()의 return값은 2

Pointer

  • C 포인터와 동일한 문법

  • 그러나 C와 달리 포인터 연산 x

  • var p *int // p는 int 포인터
    i := 42
    p = &i
    fmt.Println(*p) // read i through the pointer p
    *p = 21         // set i through the pointer p
    

Struct

  • collection of fields
package main

import "fmt"

type Vertex struct {
	X, Y int
}

func main() {
	v := Vertex{1, 2}
	v.X = 4
    fmt.Println(v)} // 4 2
	p := &v // 포인터도 가능
	p.X = 1e9 // 포인터 이용 field 접근 문법 다름
	// cf. C의 p->X 또는 (*p).x 와 동일
	// Go에서 (*p).X = 1e9도 가능하긴 하지만 위의 방식이 더 간결
	fmt.Println(v)} // 4 2
}
  • Struct literals : javascript의 객체 literal과 유사

    // 세가지 모두 가능
    var (
    	v1 = Vertex{1, 2}  // argument 순서 상관 o
    	v2 = Vertex{X: 1}  // Y:0 is implicit, argument 순서 상관 x
    	v3 = Vertex{}      // X:0 and Y:0
    )
    

Array

  • var 변수명 [크기]type
  • 크기 고정. resize 불가 => slice 활용
func main() {
	var a [2]string
	a[0] = "Hello"
	a[1] = "World"
	primes := [6]int{2, 3, 5, 7, 11, 13} // Array literal
	fmt.Println(a)
	fmt.Println(primes)
}

Slice

  • slice : []type (크기 지정 x)

  • dynamic size

  • array보다 훨씬 많이 사용

  • slice는 array에 대한 참조일뿐. 데이터를 저장하진 않음

    • array에서 앞뒤 bound에 핀만 하나씩 꽂아 놓는 느낌
    • slice 이용해서 값 변경 시 array 데이터 변경 발생
  • slice literal은 array literal에서 크기값만 빠진 형태

    package main
      
    import "fmt"
      
    func main() {
    	names := [4]string{
    		"John",
    		"Paul",
    		"George",
    		"Ringo",
    	}
    	a := names[0:2]
    	b := names[1:3]
      
    	b[0] = "XXX"
    	fmt.Println(a, b) // [John XXX] [XXX George] 
    	fmt.Println(names) // [John XXX George Ringo]
        
        
    	q := []int{2, 3, 5, 7, 11, 13} // slice literal
    	s := []struct { // struct에 대한 slice도 이런식으로 작성 가능
    		i int
    		b bool
    	}{
    		{2, true},
    		{3, false},
    		{5, true},
    		{7, true},
    		{11, false},
    		{13, true},
    	}
      
    	// 앞뒤 bound 생략 가능 (python과 유사)
    	var a [10]int
    	a[:2] // == a[0:2]
    	a[1:] // == a[1:10]
    	a[:] // == a[0:10]
    }
    
  • slice는 length와 capacity를 가진다

    • length : slice가 실제로 가리키는 길이(slice시작~slice끝). len(s)
    • capacity : slice 시작~slice의 대상이 되는 array끝. cap(s)
    • capacity를 넘는 upper bound를 걸 수 없다.
    package main
    
    import "fmt"
      
    func main() {
    	s := []int{2, 3, 5, 7, 11, 13} // len=6 cap=6 [2 3 5 7 11 13]
    	s = s[:0] // len=0 cap=6 []
    	s = s[:4] // len=4 cap=6 [2 3 5 7]
    	s = s[2:] // len=2 cap=4 [5 7]
    	s = s[:4] // len=4 cap=4 [5 7 11 13]
    	s = s[:5] // runtime error: slice bounds out of range [:5] with capacity 4
    }
    
  • Zero value = nil ( null pointer 역할)

    var s []int
    fmt.Println(s, len(s), cap(s), s == nil) // [] 0 0 true
    
  • make : built-in function

    • java new와 유사
    • array 안에 값을 모두 zero value로 초기화
    func main(){
    	a := make([]int, 5) // len=5 cap=5 [0 0 0 0 0]
    	b := make([]int, 0, 5) // len=0, cap=5 []
    }
    
  • append : func append(s []T, vs ...T) []T

    • built-in function

    • append로 인해 값을 저장하는 array의 capacity를 초과한 경우 새 array까지 자동으로 할당

      func main() {
      	var s []int // len=0 cap=0 []
      	s = append(s, 0) //len=1 cap=1 [0]. nil에도 작동
      	s = append(s, 2, 3, 4) // len=5 cap=6 [0 1 2 3 4], 
      	s = s[:6] // len=6 cap=6 [0 1 2 3 4 0] 
      }
      
  • range로 순회가능 (map에도 적용 가능)

    func main() {
    	pow := []int{1, 2, 4, 8, 16, 32, 64, 128}
    	for i, v := range pow { 
        	// range의 return 값: 2개(index, value(복사본))
        	// v 변경해도 pow에 변화 없음
    		fmt.Printf("2**%d = %d\n", i, v)
    	}
    	for i := range pow {
        	// index만 가져옴
      	}
    }
    

Map

  • zero value = nil

  • nil에는 key 추가 불가능 ( assignment to entry in nil map 발생)

    func main() {
      	
    	m := make(map[string]Vertex)
    	m["Bell Labs"] = Vertex{
    		40.68433, -74.39967,
    	}
    	fmt.Println(m["Bell Labs"])
        
    	// map literals
    	var m1 = map[string]Vertex{
    		"Bell Labs": Vertex{
    			40.68433, -74.39967,
    		},
    		"Google": Vertex{
    			37.42202, -122.08408,
    		},
    	}
      
    	// 또 다른 map literals (Vertex 생략 가능)
    	var m2 = map[string]Vertex{
        	"Bell Labs": {40.68433, -74.39967},
        	"Google": {37.42202, -122.08408},
    	}
    }
    
  • key 접근

    • value 2개 일 때 두번째 값은 키의 존재여부(bool)
    func main() {
    	m := make(map[string]int)
    	m["Answer"] = 42 // 할당
    	m["Answer"] = 48 // 변경
    	delete(m, "Answer") // 삭제
    	v, ok := m["Answer"]  // 0, false
    	m["Answer"] = 0 
    	v, ok = m["Answer"] // 0, true
    }
    

Function value

  • 함수도 다른 변수들처럼 함수 argument 및 return 값으로 사용 가능

  • 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(compute(hypot)) // 5
    	fmt.Println(compute(math.Pow)) // 81
    }
      
    

Function closure

  • adder() 함수는 closure. sum 변수에 bind 되어 있음
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),
		)
	}
}

References

Tags:

Categories:

Updated:

Leave a comment