Golang { Syntax int := 10 }

Golang Syntax _8


31. 셀렉트

package main
import "fmt"

func fibonacci(c, quit chan int) {
    var x, y int = 0, 1 
    for {
        select {
        case c <- x:
            x, y = y, x+y
        case <-quit:
            fmt.Println("quit")
            return
        }
    }
}

func main() {
    c := make(chan int)
    quit := make(chan int)
    go func() {
        for i := 0; i < 10; i++ {
            fmt.Println(<-c)
        }
        quit <- 0
    }()
    fibonacci(c, quit)
}
  • Go의 Select문은 고루틴이 다수의 통신 동작으로부터 수행 준비를 대기하게 해줌.
  • Select는 Case구문으로 받는 통신동작들 중 하나가 수행될 수 있을때 까지 대기함.
  • 다수의 채널이 동시에 준비되면 그 중 하나를 무작위로 선택함.

32. 셀렉트의 디폴트 케이스

package main
import (
    "fmt"
    "time"
)

func main() {
    tick := time.Tick(1e6)
    boom := time.After(5e8)
    for {
        select {
        case <-tick:
            fmt.Println("tick.")
        case <-boom:
            fmt.Println("BOOM!")
            return
        default:
            fmt.Println("    .")
            time.Sleep(5e7)
        }
    }
}
  • select의 default 케이스는 현재 준비가 완료된 케이스가 없을 때 수행된다.
  • 블록킹( 비동기적 ) 송/수신을 하고자 할때 default케이스를 사용할수 있다.

 

33. 연습문제

https://go-tour-kr.appspot.com/#69 참고

package main

import (
    "code.google.com/p/go-tour/tree"
	"fmt"
)

// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int) {
	_walk(t, ch)
	close(ch)
	
}

func _walk(t *tree.Tree, ch chan int) {
	if t != nil {
		_walk(t.Left, ch)
		ch <- t.Value
		_walk(t.Right, ch)
	}
}

// Same determines whether the trees
// t1 and t2 contain the same values.
func Same(t1, t2 *tree.Tree) bool {
	ch1 := make(chan int)
	ch2 := make(chan int)
	go Walk(t1, ch1)
	go Walk(t2, ch2)
	for i := range ch1 {
		if i != <- ch2 {
			return false
		}
	}
	return true	
}

func main() {
	//tree.New(2)
	ch := make(chan int)
	go Walk(tree.New(1), ch)
	for v := range ch {
		fmt.Print(v)
	}
	fmt.Println(Same(tree.New(1), tree.New(1)))
	fmt.Println(Same(tree.New(1), tree.New(2)))
}

 

Golang { Syntax int := 8 }

Golang Syntax _8


28. 고루틴

package main
import (
    "fmt"
    "time"
)

func say(s string) {
    for i := 0; i < 5; i++ {
        time.Sleep(1 * time.Millisecond)
        fmt.Println(s)
    }
}

func main() {
    go say("world")
    say("hello")
}
/**** Output ****
hello
world
world
hello
world
hello
hello
world
hello
****************/
  • Goroutines은 런타임중에 관리되는 경략 쓰레드이다.
  • go f(a, b, c)와같이 사용되며, 새로운 고루틴을 실행한다.
  • 고루틴은 동일한 주소공간에서 실행되므로, 공유되는 자원의 접근은 반드시 동기화되야한다.

 

29. 채널

채널은 채널 연산자 <-를 이용해 값을 주고 받을 수 있는, 타입이 존재하는 파이프이다.

package main
import "fmt"

func sum(a []int, c chan int) {
    sum := 0
    for _, v := range a {
        sum += v
    }
    c <- sum // send sum to c
}

func main() {
    a := []int{7, 2, 8, -9, 4, 0}

    c := make(chan int)
    go sum(a[:len(a)/2], c)
    go sum(a[len(a)/2:], c)
    x, y := <-c, <-c // receive from c

    fmt.Println(x, y, x+y) // -5 17 12
}
  • ch <- v : v를 ch로 보낸다.
    v := <-ch : ch로부터 값을 받아서 v로 넘긴다.
  • 맵이나 슬라이스처럼, 채널은 사용되기 전에 생성 되어야한다.
    ch := make(chan int)
  • 송/수신은 상대편이 준비될때까지 대기한다.
  • 채널은 버퍼링될 수 있다. make에서 두번째 인자로 버퍼의 용량을 지정해 줌으로써 해당 버퍼만큼 버퍼링되는 채널을 생성할 수 있다.
    ch := make(chan int, 100)

    package main
    
    import "fmt"
    
    func main() {
        c := make(chan int, 2)
        c <- 1
        c <- 2
        fmt.Println(<-c)
        fmt.Println(<-c)
    }

    만약 버퍼를 1을 잡게 된다면 에러가 발생하게 된다.

 

30. Range와 Close

데이터 송신측은 더 이상 보낼 값이 없음을 알리기 위해 채널을 close할 수 있다.
수신자는 아래와 같은 수신 코드의 두번째 인자를 줌으로써 채널이 닫혓는지 테스트 할 수 있다.
v, ok := <- ch와 같이 사용하며, 만약 채널이 닫겨있다면 okfalse가 들어가게 된다.
또한 for i := range ch와 같은 반복문은 채널이 닫힐 때 까지 계속해서 값을 받는다.

package main
import (
    "fmt"
)

func fibonacci(n int, c chan int) {
    x, y := 0, 1
    for i := 0; i < n; i++ {
        c <- x
        x, y = y, x+y
    }
    close(c)
}

func main() {
    c := make(chan int, 10)
    go fibonacci(cap(c), c)
    for i := range c {
        fmt.Println(i)
    }
}
  • 주의할 점은 채널은 송신측만 닫을수 있으며, 수신측에선 닫을 수 없다.
  • 채널은 파일과 다르기에 항상 닫을 필요가 없으며,
    채널을 닫는 동작은 오로지 수신측에게 더이상 보낼값이 없다고 말해야 할 때만 행해지면 된다.

 

 

Golang { Syntax int := 7 }

Golang Syntax _7


24. 웹서버

package main
import (
    "fmt"
    "net/http"
)

type Hello struct{}

func (h Hello) ServeHTTP(
    w http.ResponseWriter,
    r *http.Request) {
    fmt.Fprint(w, "Hello!")
}

func main() {
    var h Hello
    http.ListenAndServe("localhost:4000", h)
}
  • Package http는 http.handler를 구현한 어떠한 값을 사용해 HTTP 요청을 제공한다.
  • Handler는 다음과 같은 인터페이스를 가진다.
    package http
    type Handler interface {
    	ServeHTTP(w ResponseWriter, r *Request)
    }

 

25. 연습문제

아래 나오는 타입을 구현하고 그 타입의 ServeHTTP 메소드를 정의하십시오. 그 메소드를 당신의 웹 서버에서 특정 경로를 처리할 수 있도록 등록하십시오.

type String string

type Struct struct {
    Greeting string
    Punct string
    Who string
}

예컨대, 당신은 아래와 같이 핸들러를 등록할 수 있어야 합니다.
http.Handle("/string", String("I'm a frayed knot."))
http.Handle("/struct", &Struct{"Hello", ":", "Gophers!"})

package main
import (
    "fmt"
    "net/http"
)

type String string

func (s String) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, s)
}

type Struct struct {
    Greeting string
    Punct string
    Who string
}

func (s *Struct) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, s.Greeting, s.Punct, s.Who)
}

func main(){
    http.Handle("/string", String("I'm silnex"))
    http.Handle("/struct", &Struct{"Hello",":","Gopher!"})
    http.ListenAndServe("localhost:4000", nil)
}

 

26. 이미지

package main
import (
    "fmt"
    "image"
)

func main() {
    m := image.NewRGBA(image.Rect(0, 0, 100, 100))
    fmt.Println(m.Bounds())
    fmt.Println(m.At(0, 0).RGBA())
}
  • Package Image는 아래와 같은 image인터페이스를 정의한다.
    package image
    
    type Image interface {
    	ColorModel() color.Model
    	Bounds() Rectangle
    	At(x, y int) color.Color
    }

 

27. 연습문제

이전의 연습에서 당신이 작성한 그림 생성기를 기억하십니까? 다른 생성기를 만들어봅시다.
하지만 이번에는 데이터의 슬라이스 대신에 image.Image 의 구현체를 반환할 것입니다.
당신 자신의 Image 타입을 정의하시고, 필수 함수들 을 구현하신 다음, pic.ShowImage 를 호출하십시오.
Bounds 는 image.Rect(0, 0, w, h) 와 같은 image.Rectangle 을 반환해야 합니다.
ColorModel 은 color.RGBAModel 을 반환해야 합니다.
At 은 하나의 컬러를 반환해야 합니다; 지난 그림 생성기에서 값 v 는 color.RGBA{v, v, 255, 255} 와 같습니다.

package main
import (
    "code.google.com/p/go-tour/pic"
    "image"
	"image/color"
)

type Image struct{
	w, h int
    colorA, colorB uint8
}


func (i *Image) ColorModel() color.Model{
    return color.RGBAModel
}

func (i *Image) Bounds() image.Rectangle {
    return image.Rect(0,0,i.w,i.h)
}

func (i *Image) At(x, y int) color.Color{
    return color.RGBA{uint8(x), uint8(y), i.colorB, i.colorA}
}

func main() {
    m := &Image{123,123,131,141}
    pic.ShowImage(m)
}

==== Output ====

Golang { Syntax int := 6 }

Golang Syntax _6


21. 메소드

package main
import (
    "fmt"
    "math"
)

type Vertex struct {
    X, Y float64
}

func (v *Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func main() {
    v := &Vertex{3, 4}
    fmt.Println(v.Abs())
}
  • Go에는 클래스가 없지만 메소드를 구조체(struct)에 붙일 수 있다.
  • 메소드 리시버(method receiver) func 키워드와 메소드 이름 사이에 인자로 들어간다.
    func 구조체이름(혹은포인터) 메소드_리시버_이름 반환타입이고 위 코드를 통해 예시를 들면,
    func (v *Vertex) Abs() float64: 구조체 VertexAbs()란 이름으로 호출 할 수 있다.
  • Go의 메소드는 구조체 뿐만아니라 모든 타입(type)에 붙일 수 있다.
    하지만, 다른 패키지에 있는 타입이나 기본 타입들에 메소드를 붙이는 것은 불가능하다.

    package main
    import (
        "fmt"
        "math"
    )
    
    type MyFloat float64
    
    func (f MyFloat) Abs() float64 {
        if f < 0 {
            return float64(-f)
        }
        return float64(f)
    }
    
    func main() {
        f := MyFloat(-math.Sqrt2)
        fmt.Println(f.Abs())
    }
    
  • 포인터 리시버는 메소드가 호출될때마다 값이 복사되는 것을막고, 메소드에서 리시버 포인터가 가르키는 값을 수정하기 위함이다.
    (함수에서 포인터를 전달하는것과 값을 전달하는 것에 차이를 생각하면 이해가 쉽다..)

    package main
    import (
        "fmt"
        "math"
    )
    
    type Vertex struct {
        X, Y float64
    }
    
    func (v *Vertex) Scale(f float64) {
        v.X = v.X * f
        v.Y = v.Y * f
    }
    
    func (v *Vertex) Abs() float64 {
        return math.Sqrt(v.X*v.X + v.Y*v.Y)
    }
    
    func main() {
        v := &Vertex{3, 4}
        v.Scale(5)
        fmt.Println(v, v.Abs()) // &{15 20} 25
    }
    

 

22. 인터페이스

package main
import (
    "fmt"
    "math"
)

type Abser interface {
    Abs() float64
}

func main() {
    var a Abser

    f := MyFloat(-math.Sqrt2)
    v := Vertex{3, 4}
    a = f  // a MyFloat implements Abser
    a = &v // a *Vertex implements Abser
    //a = v  // a Vertex, does NOT
    // implement Abser

    fmt.Println(a.Abs()) // 5
}

/****** MyFloat ******/
type MyFloat float64

func (f MyFloat) Abs() float64 {
    if f < 0 {
        return float64(-f)
    }
    return float64(f)
}

/****** Vertex ******/
type Vertex struct {
    X, Y float64
}

func (v *Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
  • 인터페이스는 메소드의 집합으로 정의된다.
    인터페이스 안에 정의되어있는 모든 메소드가 구현되어있는 타입의 값은 모두 인터페이스 타입의 값이 될수 있다.
  • 인터페이스의 메소드가 이미 정의 되어있는 패키지 혹은 타입에 대해서 따로 명시적으로 표현할 필요가 없다.
    패키지 io에 Reader와 Writer가 정의되어 있기 때문에 따로 정의할 필요가없다.

    package main
    import (
        "fmt"
        "os"
    )
    
    type Reader interface {
        Read(b []byte) (n int, err error)
    }
    
    type Writer interface {
        Write(b []byte) (n int, err error)
    }
    
    type ReadWriter interface {
        Reader
        Writer
    }
    
    func main() {
        var w ReadWriter
        //var w Writer
    
        // os.Stdout implements Writer
        w = os.Stdout
    
        fmt.Fprintf(w, "hello, writer\n")
    }
    

 

22. 에러

package main
import (
    "fmt"
    "time"
)

type MyError struct {
    When time.Time
    What string
}

func (e *MyError) Error() string {
    return fmt.Sprintf("at %v, %s",
        e.When, e.What)
}

func run() error {
    return &MyError{
        time.Now(),
        "it didn't work",
    }
}

func main() {
    if err := run(); err != nil {
        fmt.Println(err) // at 2009-11-10 23:00:00 +0000 UTC m=+0.000000001, it didn't work
    }
}
  • Go에선 문자열을 반환하는 하나의 메소드 Error()로 구성된 내장 인터페이스 타입 error를 사용한다.
    type error interface {
         Error() string
    }
  • 기본적으로 모든 에러는 error interface를 호출하지만, 위 코드 처럼 직접 에러를 정의할 수 있다.
  • + 이해가 힘들어 추가적인 내용 첨부 [예제로 배우는 Go 프로그래밍]

 

23. 연습문제

Sqrt 함수는 복소수를 지원하지 않기 때문에, 음수가 주어지면 nil 이 아닌 에러 값을 반환해야 합니다.


새로운 타입을 만드십시오.

type ErrNegativeSqrt float64

and make it an error by giving it a 그리고 아래 메소드를 구현함으로써 그 타입이 error 가 되게 하십시오.

func (e ErrNegativeSqrt) Error() string

이는 ErrNegativeSqrt(-2).Error() 가 "cannot Sqrt negative number: -2" 를 반환하는 그러한 메소드입니다.


package main
import (
    "fmt"
    "math"
)

type ErrNegativeSqrt float64

func (e ErrNegativeSqrt) Error() string {
    if e < 0 {
    	return fmt.Sprintf("cannot Sqrt negative number: %0.1f", e)
    }
    return fmt.Sprintf("OK")
}


func Sqrt(f float64) (float64, error) {
    if f > 0 {
	    var a, z, c float64 = 0, 1, 0
	    for a = 1; a < 10; a++ {
	        if math.Abs(z - c) > 1e-10 {
    	        c = z
        	    z = z - (z * z - f)/(2*z)
	        } else {
    	        break
        	}
    	}
    	return z, ErrNegativeSqrt(f)
    } else {
        return f, ErrNegativeSqrt(f)
    }
    
}

func main() {
    fmt.Println(Sqrt(2))
    fmt.Println(Sqrt(-2))
}

 

Golang { Syntax int := 5 }

Golang Syntax _5


19. 스위치

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.")
    }
}
  • Go의 Switch는 break문을 통해 빠져나오지 않고 조건이 일치하는 case문을 마치면 switch문을 종료하게된다.
  • 위의 코드를 보면 만약 time.Saturdaytoday + 0와 일치하면 today + 1today + 2, default문은 실행하지 않는다.
  • Go의 switch에선 조건을 생략할 수 있는데 이를 이용해 if-then-else문을 깔끔하게 작성할 수 있다.
    switch옆에 조건이 없는 대신 case문에 조건문이 들어가게되며, 제일 위쪽부터 true인 조건의 case문을 실행한다.

    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.")
        }
    }
    

 

20. 연습문제

complex64 타입과 complex128 타입을 통해서 Go 언어의 복소수 지원 기능을 알아봅니다.
세제곱근을 얻기 위해서는, 뉴턴의 방법 (Newton’s method)을 적용하여 다음을 반복 수행합니다:

z = z - (z * z * z - x) / (3 * z * z)

package main
import "fmt"

func Cbrt(x complex128) complex128 {
    var z complex128 = 1
    for a := 1; a < 10; a++ {
        z =  z - (z * z * z - x) / (3 * z * z)
    }
    return z
}

func main() {
    fmt.Println(Cbrt(3)) // (1.4422495703074083+0i)
}

Golang { Syntax int := 4 }

Golang Syntax _4


15. 맵 (Maps)

package main
import "fmt"

type Vertex struct {
    Lat, Long float64
}

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

func main() {
    fmt.Println(t, m) // map[Bell Labs:{40.68433 -74.39967} Google:{37.42202 -122.08408}]
}
  •  Go에서 맵은 값에 key를 지정한다.
  • 맵은  반드시 사용하기 전에  make를 명시해아한다.
  • make를 수행하지 않은 nil 에는 값을 할당할 수 없다.
  • 만약 상위의 타입이 타입명이라면 리터럴에서 타입명을 생략해도 된다.
    즉 "Bell Labs": {40.68433, -74.39967} 와 "Bell Labs": Vertex{40.68433, -74.39967}는 같은 뜻이다.
  • 맵 다루기 >
        맵의 요소를 생성/ 수정 :  m[key] = data
        요소값 가져오기 : val = m[key]
        키 존재 여부 확인 : val, has_key = m[key] >>  만약 m[key]가 존재하지 않는다면 has_keyfalse가, valzero value이 할당된다.

16. 연습문제

WordCount 함수를 구현합니다. 이 함수는 s라는 문자열 내에서 각각의 “단어”의 등장 횟수를 나타내는 맵을 반환해야 합니다. wc.Test 함수는 주어진 함수를 이용하여 테스트를 실행한 뒤에 그 성공 여부를 출력해 줍니다.

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

func WordCount(s string) map[string]int {
    m := make(map[string]int)
    for _, key := range strings.Fields(s) {
        _, ok := m[key]
        if ok {
            m[key]++
        } else {
            m[key] = 1
        }
    }
    return m
}

func main() {
    wc.Test(WordCount)
}

 

17. 함수 값과 클로저

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),
        )
    }
}
  • Go에서  함수를 value(값)이다. 값이기 때문에 변수에 저장해 사용할 수 있다.
  • 함수는 클로저(full closures)이다. 위 예제에서 보면 각각의 pos와  neg의  sum 변수가 존재하는 걸 볼 수있다.

18. 연습문제

fibonacci 함수를 구현합니다. 이 함수는 이어지는 피보나치 수를 반환하는 함수 (클로져)를 반환해야 합니다.

package main
import "fmt"

// fibonacci is a function that returns
// a function that returns an int.
func fibonacci() func() int {
    x1 := 1
    x2 := 2
    return func() int {
        x3 := x1+x2
        x1, x2 = x2, x3
        return x3
    }
}

func main() {
    f := fibonacci()
    for i := 0; i < 10; i++ {
        fmt.Println(f())
    }
}

 

Golang { Syntax int := 3 }

Golang Syntax _3


8. 기본 자료형

Go에 존재하는 자료형

  • bool : true 혹은 false를 나타내는 부울 타입이다.
  • string : 문자열
  • int [ int8, int16, int32, int64 ]
  • uint [ uint8, uint16, uint32, uint64 ]
  • rune [ int32의 alias이다. ] : 대개 유니코드 코드 포인트 값을 표현하는데 사용된다.
  • float32, float64
  • complex64, complex128

+ Go에선 Type casting을 위해선 uint8(10)과 같이 castring 할 수 있다.

 

9. 구조체 (struct)

type Vertex struct {
    X int
    Y int
}

func main() {
    v := Vertex{1, 2}
    v.X = 4
    fmt.Println(v.X)
}
  • type 구조체이름 struct { ... } 으로 정의하고, 구조체이름( ... )으로 선언한다.
  • 구조체 필드에 속한 데이터는 dot(.)으로 접근할 수 있다.
  • 구조체 리터럴(Struct Literals)은 필드와 값을 나열해 구조체를 새로 할당하는 방법이다.
    순서의 상관없이 {name: value}구문을 통해 할당 할 수 있다.

    package main
    import "fmt"
    
    type Vertex struct {
        X, Y int
    }
    
    var (
        p = Vertex{1, 2}  // has type Vertex
        r = Vertex{X: 1}  // Y:0 is implicit
        s = Vertex{}      // X:0 and Y:0
    )
    
    func main() {
        fmt.Println(p, r, s) // {1 2} {1 0} {0 0}
    }
    

     

10. 포인터

Go에선 포인터는 & 접두어로 사용되며, C처럼 포인터 연산은 지원하지 않는다.

package main

import "fmt"

type Vertex struct {
    X int
    Y int
}

func main() {
    p := Vertex{1, 2}
    q := &p
    q.X = 1e9
    fmt.Println(p) // {1000000000 2}
}
  • C에서의 동작과 유사하게 돌아간다. ( 포인터를 통해 접근시 가르키고 있는 데이터도 변경이 이루어짐 )
  • 포인터 연산이 안되니 오히려 C++의 참조자와 비슷한것아닌지…(주관적인생각)

11. new 함수

new(T)는 모든 필드가 0(Zero value)이 할당된 T 타입의 포인터를 반환한다.
(Zero value는 숫자 타입에서는 0, 참조 타입에선 nil을 뜻한다.)

package main
import "fmt"

type Vertex struct {
    X, Y int
}

func main() {
    v := new(Vertex)
    // or var v *Vertex = new(Vertex)
    fmt.Println(v)
    v.X, v.Y = 11, 9
    fmt.Println(v) // &{0 0} &{11 9}
}

 

12. 슬라이스 (Slices)

슬라이스란 Go에서의 배열의 값을 가르킨다. 그리고 배열의 길이(length)를 가지고 있다.

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])

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

    // missing high index implies len(s)
    fmt.Println("p[4:] ==", p[:])
    /** Output **
    p == [2 3 5 7 11 13]
    p[1:4] == [3 5 7]
    p[:3] == [2 3 5]
    p[4:] == [2 3 5 7 11 13]
    */
}
  • 슬라이스는 []T이렇게 선언 할 수 있으며 T 타입의 요소를 가지는 슬라이스이다.
  • 슬라이스에 저장된 데이터를 가져올때 [0보다 큰값:슬라이스의 길이보다 작은 값]을 통해서 부분적으로 잘라서 가져올 수 있다.
  • [lo:hi]일 경우 슬라이스의 lo번째 데이터에서 hi-1 번째 데이터 까지 가져온다. 전체를 가져오고 싶은 경우, 
    [:]을 통해서 모든 값을 가져 올 수 있다.
  • 슬라이스는 make(타입, 길이, 용량)함수를 통해서 만들 수 있다.
    cap()함수는 슬라이스의 용량(capacity)를 가져온다.

    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)
        fmt.Println(b[:4])
    }
    
    func printSlice(s string, x []int) {
        fmt.Printf("%s len=%d cap=%d %v\n",
            s, len(x), cap(x), x)
    }
    /** Output **
    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]
    [0 0 0 0]
    */
  • 빈 슬라이스는 슬라이스의 zero value가 nil 이다.
  • nil 슬라이스는 길이와 용량이 이다.
    package main
    import "fmt"
    
    func main() {
        var z []int
        fmt.Println(z, len(z), cap(z))
        if z == nil {
            b := [...]string{"Penn", "Teller"}
            fmt.Println("nil!", cap(b))
        }
    }
    /** Output **
    [] 0 0
    nil! 2
    */

 

13. 레인지 Range

for 반복문에서 range를 사용하면 슬라이스나 맵을 순회(iterates)할 수있다.

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)
    }
}
/** Output **
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 index, value := range pow {...}에서 index와 value는 슬라이스의 index와 value를 의미한다.
  • 만약 index나 value를 무시하려면 _를 통해서 무시하고 진행 할 수 있다.
    for _, v := range pow {...} or for i := range pow {...}

 

14. 슬라이스 연습

Pic이라는 함수를 구현합니다. 이 함수는 dy개 만큼의 길이를 가지는 슬라이스를 리턴해야 하는데, 각각의 요소들은 또한 dx 개의 8비트 부호없는 8비트 정수 타입을 가지는 슬라이스입니다. 프로그램을 실행하면 이 정수값들을 흑백 (사실은 파란색)을 나타내는 값으로 해석하여 그림을 보여줄 것입니다.

package main
import "code.google.com/p/go-tour/pic"
	
func Pic(dx, dy int) [][]uint8 {
    y := make([][]uint8, dy, dy)
    
    for i := 0; i < int(dy); i++{
    	y[i] = make([]uint8, dx, dx)
    }
    for i, pe := range y {
        for x := 0; x < dx; x++{
            pe[x] = uint8(x) ^ uint8(i)
        }
    }
	return y
}

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

=== Output ===

 

Golang { Syntax int := 2 }

Golang Syntax _2


5. 반복문

package main
import "fmt"

func main() {
    sum := 0
    for i := 0; i < 10; i++ {
        sum += i
    }
    fmt.Println(sum)
}
  • Go에선 반복엔 for만이 존재하며 while 은 사용하지 않는다.
  • for 반복문은 C와 유사하지만 소괄호는 사용하지 않는다. 하지만 실행문을 위해선 중괄호가 필요하다.
  • for sum < 2000 { ... }
    C처럼 전, 후처리문을 제외한 조건문만을 사용할 수 있다. (while과 유사하다.)
  • for { ... }
    무한 루프의 경우 아무 조건을 작성하지 않으면 된다.

 

6. 조건문

package main
import (
    "fmt"
    "math"
)

func sqrt(x float64) string {
    if x < 0 {
        return sqrt(-x) + "i"
    }
    return fmt.Sprint(math.Sqrt(x))
}

func main() {
    fmt.Println(sqrt(2), sqrt(-4))
}
  • for(반복문)과 유사하게 조건의 소괄호 ( ) 을 사용하지 않으며, 실행문을 위한 중괄호 { }가 필요하다.
    다만 C 와 다른점은 실행문이 한줄뿐이더라도 반드시 중괄호{ } 가 필요하다.
  • if v := math.Pow(x, n); v < lim{ ... }
    if의 조건문 앞에 짧은 문장을 실행 할 수 있다.
  • if의 짧은 명령문에서 선언된 변수는 else if, else구문에서도 사용할 수 있다.
    package main
    import (
        "fmt"
        "math"
    )
    
    func pow(x, n, lim float64) float64 {
        if v := math.Pow(x, n); v < lim {
            return v
        } else if v == lim {
            fmt.Printf("%g == %g\n", v, lim)
        } else {
            fmt.Printf("%g >= %g\n", v, lim)
        }
        // can't use v here, though
        return lim
    }
    
    func main() {
        fmt.Println(
            pow(3, 2, 10),
            pow(3, 3, 27),
        )
    }
    

7. 연습문제[src]

7-1 함수와 루프의 사용법을 익히는 간단한 연습으로, 제곱근 함수를 뉴턴의 방법(Newton’s method)을 이용하여 구현합니다.
여기서 뉴턴의 방법이란 초기값 z를 선택한 후에 다음의 공식을 이용하여 반복적으로 Sqrt(x) 함수의 근사값을 찾아가는 방법을 말합니다:
z = z - (z * z - x) / (2 * z)
처음에는 계산을 10번만 반복하여 여러분이 작성한 함수가 다양한 값들 (1, 2, 3, …)에 대하여 얼마나 정확한 값을 찾아내는지 확인합니다.

7-2 루프의 조건을 수정하여 값이 더이상 바뀌지 않을 때 (혹은 아주 작은 차이가 발생할 때) 루프를 종료하도록 합니다. 이렇게 하면 반복문의 실행 횟수가 어떻게 달라지는지 확인합니다. 결과값이 math.Sqrt함수의 값과 얼마나 비슷한가요?

package main
import (
    "fmt"
    "math"
)

func Sqrt(x float64) float64{
    var a, z, c float64 = 0, 1, 0
    for a = 1; a < 10; a++ {
        if math.Abs(z - c) > 1e-10 {
            c = z
            z = z - (z * z - x)/(2*z)
        } else {
            break
        }
    }
    return z
}

func main() {
    fmt.Println(Sqrt(2))    
}