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),
)
}
}
Leave a comment