본문 바로가기
개발~

Go tour

by 보배곰 2021. 8. 9.

https://tour.golang.org/welcome/1

package main

import "fmt"

func main() {
    fmt.Println("Hello, 세계")
}

Internal Directories

Code in or below a directory named "internal" is importable only by code in the directory tree rooted at the parent of "internal". Here's an extended version of the directory layout above:

/home/user/go/
    src/
        crash/
            bang/              (go code in package bang)
                b.go
        foo/                   (go code in package foo)
            f.go
            bar/               (go code in package bar)
                x.go
            internal/
                baz/           (go code in package baz)
                    z.go
            quux/              (go code in package main)
                y.go

The code in z.go is imported as "foo/internal/baz", but that import statement can only appear in source files in the subtree rooted at foo. The source files foo/f.go, foo/bar/x.go, and foo/quux/y.go can all import "foo/internal/baz", but the source file crash/bang/b.go cannot.

Packages

모든 Go 프로그램은 패키지로 구성되어 있습니다.

프로그램은 main 패키지에서 실행을 시작합니다.

Imports

This code groups the imports into a parenthesized, "factored" import statement.

package main

import (
    "fmt"
    "math"
)

func main() {
    fmt.Printf("Now you have %g problems.\n", math.Sqrt(7))
}

You can also write multiple import statements, like:

import "fmt"
import "math"

But it is good style to use the factored import statement.

Exported names

In Go, a name is exported if it begins with a capital letter. When importing a package, you can refer only to its exported names. Any "unexported" names are not accessible from outside the package.

Functions

A function can take zero or more arguments.

Notice that the type comes after the variable name.

package main

import "fmt"

// func add(x, y int) int {
func add(x int, y int) int {
    return x + y
}

func main() {
    fmt.Println(add(42, 13))
}

Multiple results

A function can return any number of results.

package main

import "fmt"

func swap(x, y string) (string, string) {
    return y, x
}

func main() {
    a, b := swap("hello", "world")
    fmt.Println(a, b)
}

Named return values

Go's return values may be named. If so, they are treated as variables defined at the top of the function. These names should be used to document the meaning of the return values.

A return statement without arguments returns the named return values. This is known as a "naked" return. 인자가 없는 return 문은 이름이 주어진 반환 값을 반환합니다. 이것을 naked return이라고 합니다.

Naked return statements should be used only in short functions, as with the example shown here. They can harm readability in longer functions.

// 추천 안함. 가독성의 문제가 있을 수 있음. 
func split(sum int) (x, y int) {
    x = sum * 4 / 9
    y = sum - x
    return
}

Variables

The var statement declares a list of variables: as in function argument lists, the type is last. A var statement can be at package or function level.

package main

import "fmt"

var c, python, java bool

func main() {
    var i int
    fmt.Println(i, c, python, java)
}

Variables with initializers

A var declaration can include initializers, one per variable.

If an initializer is present, the type can be omitted; the variable will take the type of the initializer.

package main

import "fmt"

var i, j int = 1, 2

func main() {
    var c, python, java = true, false, "no!"
    fmt.Println(i, j, c, python, java)
}

Short variable declarations

Inside a function, this := short assignment statement can be used in place of a var declaration with implicit type.

Outside a function, every statement begins with a keyword(var, func and so on) and so the := construct is not available.

package main

import "fmt"

func main() {
    var i, j int = 1, 2
    k := 3
    c, python, java := true, false, "no!"

    fmt.Println(i, j, k, c, python, java)

}

Basic types

The int, uint, and uintptr types ar usually 32 bits wide on 32-bit systems and 64 bits wide on 64-bit systems. When you need an integer value you should use int unless you have a specific reason to use a sized or unsigned integer type.

bool

string

int  int8  int16  int32  int64
uint uint8 uint16 uint32 uint64 uintptr

byte // alias for uint8

rune // alias for int32
     // represents a Unicode code point

float32 float64

complex64 complex128
package main

import (
    "fmt"
    "math/cmplx"
)

var (
    ToBe   bool       = false
    MaxInt uint64     = 1<<64 - 1
    z      complex128 = cmplx.Sqrt(-5 + 12i)
)

func main() {
    fmt.Printf("Type: %T Value: %v\n", ToBe, ToBe)
    fmt.Printf("Type: %T Value: %v\n", MaxInt, MaxInt)
    fmt.Printf("Type: %T Value: %v\n", z, z)
}

Zero values

Variables declared without an explicit initial value are given their zero value.

The zero value is:

0 for numeric types,

false for the boolean type, and

"" (the empty string) for strings.

package main

import "fmt"

func main() {
    var i int
    var f float64
    var b bool
    var s string
    fmt.Printf("%v %v %v %q\n", i, f, b, s)  // 0 0 false ""
}

Type conversions

The expression T(v) converts the value v to the type T.

var i int = 42
var f float64 = float64(i)
var u uint = uint(f)

i := 42
f := float64(i)
u := uint(f)

Type inference

When declaring a variable without specifying an explicit type (either by using the := syntax or var = expression syntax), the variable's type is inferred from the value on the right hand side.

package main

import "fmt"

func main() {
    v := 3.14 // change me!
    fmt.Printf("v is of type %T\n", v)
}

Constants

Constants are declared like variables, but with the const keyword. Constants can be character, string, boolean, or numeric values. Constants cannot be using the := syntax.

package main

import "fmt"

const Pi = 3.14

func main() {
    const World = "世界"
    fmt.Println("Hello", World)
    fmt.Println("Happy", Pi, "Day")

    const Truth = true
    fmt.Println("Go rules?", Truth)
}

Numeric Constants

Numeric constants are high-precision values. An untyped constant takes the type needed by its context. Try printing needInt(Big) too. (An int can store at maximum a 64-bit integer, and sometimes less.)

package main

import "fmt"

const (
    // Create a huge number by shifting a 1 bit left 100 places.
    // In other words, the binary number that is 1 followed by 100 zeroes.
    Big = 1 << 100
    // Shift it right again 99 places, so we end up with 1<<1, or 2.
    Small = Big >> 99
)

func needInt(x int) int { return x*10 + 1 }
func needFloat(x float64) float64 {
    return x * 0.1
}

func main() {
    fmt.Println(needInt(Small))
    fmt.Println(needFloat(Small))
    fmt.Println(needFloat(Big))
}

For

Go has only one looping construct, the for loop.

  • the init statement: executed before the first iteration
  • the condition expression: evaluated before every iteration
  • the post statement: executed at the end of every iteration

the for statement and the braces { } are always required.

package main

import "fmt"

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

The init and post statements are optional.

pakcage main

import "fmt"

func main() {
    sum := 1
    for ; sum < 1000 ; {
        sum += sum;
    }
    fmt.Println(sum)
}

For is Go's "while"

package main

import "fmt"

func main() {
    sum := 1
    for sum < 1000 {
        sum += sum
    }
    fmt.Println(sum)
}

infinite loop

package main

import "fmt"

func main() {
    for {
    }
}

-> timeout running program

If

if 처럼 () 는 안 써도 되고, {} 는 필수

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

If with a short statement

Like for the if statement can start with a short statement to execute before the condition. Variables declared by the statement are only in scope until the end of if.

package main

import (
    "fmt"
    "math"
)

func pow(x, n, lim float64) float64 {
    if v:= math.Pow(x,n); v < lim {
        return v
    }
    return lim
}

func main() {
    fmt.Println(
        pow(3, 2, 10),
        pow(3, 3, 20)
    )
}

If and else

Variables declared inside an if short statement are also available inside any of the else blocks.

package main

import (
    "fmt"
    "math"
)

func pow(x, n, lim float64) float64 {
    if v:= math.Pow(x, n); v < lim {
        return v
    } else {
        fmt.Println("%g >= %g\n", v, lim)
    }
    return lim
}

func main() {
    fmt.Println(
        pow(3, 2, 10),
        pow(3, 3, 20)
    )
}

Exercise: Loops and Functions

https://go-tour-ko.appspot.com/flowcontrol/8

Switch

A switch statement is a shorter way to write a sequence of if - else statements. It runs the first case whose value is equal to the condition expression.

Go's switch is like the one in C, C++, Java, JavaScript, and PHP, except that Go only runs the selected case, not all the cases that follow. In effect, the break statement that is needed at the end of each case in those languages is provided automatically in Go. Another important difference is that Go's switch cases need not be constants, and the values involved need not be integers.

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

Switch evaluation order

Switch cases evaluate cases from top to bottom, stopping when a case succeeds.

For example, does not call f if i==0.)

switch i {
    case 0:
    case f():
}
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.")
    }
}

Switch with no condition

Switch without a condition is the same as switch true. 조건이 없는 Switch는 switch true와 동일합니다.

This construct can be a clean way to write long if-then-else chains.

pacakge main

import (
    "fmt"
    "time"
)

func main() {
    t := time.Now()
    swich {
    case t.Hour() < 12:
        fmt.Println("Good morning!")
    case t.Hour() < 17:
        fmt.Println("Good afternoon.")
    default:
        fmt.Println("Good evening.")
    }
}

Defer

A defer statement defers the execution of a function until the surrounding function returns. defer문은 자신을 둘러싼 함수가 종료할 때까지 어떠한 함수의 실행을 연기합니다.

The deferred call's arguments are evaluated immediately, but the function call is not executed until the surrounding function returns. 연기된 호출의 인자는 즉시 평가되지만 그 함수 호출은 자신을 둘러싼 함수가 종료할 때까지 수행되지 않습니다.

package main

import "fmt"

func main() {
    defer fmt.Println("world")

    fmt.Println("hello")
}

실행결과

hello
world

Stacking defers

Deferred function calls are pushed onto a stack. When a function returns, its deferred calls are executed in last-in-first-out order. 연기된 함수 호출들은 스택에 쌓입니다. 한 함수가 종료될 때 그것의 연기된 함수들을 후입선출 순서로 수행됩니다.

package main

import "fmt"

func main() {
    fmt.Println("counting")

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

    fmt.Println("done")
}

실행결과

counting
done
9
8
7
6
5
4
3
2
1
0

Pointers

Go has pointers. A pointer holds the memory address of value. The type *T is a pointer to a T value. It's zero value is nil. Go는 포인터를 지원합니다. 포인터는 값의 메모리 주소를 가지고 있습니다. *T 타입은 T 값을 가리키는 포인터입니다. 이것의 zero value는 nil 입니다.

var p *int

The & operator generates a pointer to its operand. & 연산자는 이것의 피연산자에 대한 포인터를 생성합니다.

i := 42
p = &i

The * operator denotes the pointer's underlying value. * 연산자는 포인터가 가리키는 주소의 값을 나타냅니다.

fmt.Println(*p)    // read i through the pointer p
*p = 21            // set i through the pointer p

This is known as "dereferencing" or "indirecting". 이것은 "역 참조" 또는 "간접 참조"로 알려져 있습니다.

Unlike C, Go has no pointer arithmetic. C언어와는 다르게, Go는 포인터 산술을 지원하지 않습니다.

package main

import "fmt"

func main() {
    i, j := 42, 2701

    p := &i    // point to i
    fmt.Println(*p)    // read i through the pointer
    *p = 21        // set i through the pointer
    fmt.Println(i)    // see the new value of i

    p = &j        // pointer to j 
    *p = *p    / 37    // divide j through the pointer
    fmt.Println(j)    // see the new value of j
}

실행결과

42
21
73

Structs

A struct is a collection of fields.

package main

import "fmt"

type Vertex struct {
    X int
    Y int
}

func main() {
    fmt.Println(Vertex{1,2})
}

실행결과

{1 2}

Struct Fields

Struct fields are accessed using a dot.

package main

import "fmt"

type Vertex struct {
    X int
    Y int
}

func main() {
    v := Vertex{1, 2}
    v.X = 4
    fmt.Println(v.X)
}

Pointers to structs

Struct fields can be accessed through a struct pointer. 구조체 포인터를 통해서 구조체 필드를 접근할 수 있습니다.

To access the field X of a struct when we have the struct pointer p we could write (*p).X. However, that notation is cumbersome, so the language permits us instead to write just p.X, without the explicit dereference. (*p).X 로 작성하면, 구조체 포인터 p에서 구조체의 X 필드에 접근할 수 있습니다. 그러나 위 표기법은 번거로울 수 있습니다. 따라서 이 언어는 역 참조를 명시할 필요 없이 p.X로 작성할 수 있습니다.

package main

import "fmt"

type Vertex struct {
    X int
    Y int
}

func main() {
    v := Vertex{1, 2}
    p := &v
    p.X = 1e9
    fmt.Println(v)
}

Struct Literals

A struct literal denotes a newly allocated struct value by listing the values of its fields. You can list just. a subset of fields by using the Name: syntax. (And the order of named fields is irrelevant.) 구조체 리터럴은 필드 값을 나열하여 새로 할당된 구조체 값을 나타냅니다. Name: 구문으로 필드의 하위 집합만 나열할 수 있습니다. (명명된 필드의 순서는 무관합니다.)

The special prefix & returns a pointer to the struct value. 특별한 접두사 & 은 구조체 값으로 포인터를 반환합니다.

package main

import "fmt"

type Vertex struct {
    X, Y int
}

var (
    v1 = Vertex{1, 2}    // has type Vertex
    v2 = Vertex{X: 1}    // Y:0 is implicit
    v3 = Vertex{}        // X:0 and Y:0
    p = &Vertex{1, 2}    //has type *Vertex
)

func main() {
    fmt.Println(v1, p, v2, v3)
}

실행 결과

{1 2} &{1 2} {1 0} {0 0}

Array

The type [n]T is an array of n values of type T. [n]T 타입은 타입이 T인 n값들의 배열입니다.

The expression

var a [10]int

declares a variable a as an array of ten integers.

An array's length is part of its type, so arrays cannot be resized. This seems limiting, but don't worry; Go provides a convenient way of working with arrays.

package main

import "fmt"

func main() {
    var a [2]string
    a[0] = "Hello"
    a[1] = "World"
    fmt.Println(a[0], a[1])
    fmt.Println(a)

    primes := [6]int{2,3,5,7,11,13}
    fmt.Println(primes)
}

Slices

An array has a fixed size. A slice, on the other hand, is a dynamically-sized, flexible view into the elements of an array. 배열은 고정된 크기를 가지고 있습니다. 반면에, 슬라이스는 배역의 요소들을 동적인 크기로, 유연하게 볼 수 있습니다. 실제로, 슬라이스는 배열보다 훨씬 흔합니다.

The type []T is a slice with elements of type T.

A slice is formed by specifying two indices, a low and high bound, separated by a colon:

a[low : high]

This selects a half-open range which includes the first elements, but excludes the last one.

The following expression creates a slice which includes elements 1 through 3 of a:

a[1:4]
package main

import "fmt"

func main() {
    primees := [6]int{2,3,5,7,11,13}

    var s []int = primes[1:4]
    fmt.Println(s)
}

실행결과

[3 5 7]

Slices are like references to arrays

A slice does not store any data, it just describes a section of an underlying array. 슬라이스는 어떤 데이터도 저장할 수 없습니다. 이것은 단지 기본 배열의 한 영역을 나타낼 뿐입니다.

Changing the elements of a slice modifies the corresponding elements of its underlying array. 슬라이스의 요소를 변경하면 기본 배열의 해당 요소가 수정됩니다.

Other slices that share the same underlying array will see those changes. 동일한 기본 배열을 공유하는 다른 슬라이스는 이러한 변경사항을 볼 수 있습니다.

package main

import "fmt"

func main() {
    names := [4]string(
        "John",
        "Paul",
        "George",
        "Ringo"
    )
    fmt.Println(names)

    a := names[0:2]
    b := names[1:3]
    fmt.Println(a, b)

    b[0] = "XXX"
    fmt.Println(a, b)
    fmt.Println(names)
}

실행 결과

[John Paul George Ringo]
[John Paul] [Paul George]
[John XXX] [XXX George]
[John XXX George Ringo]

Slice literals

A slice literal is like an array literal without the length.

This is an array literal:

[3]bool{true, true, false}

And this creates the same array as above, then builds a slice that references it:

[]bool{true, true, false}
package main

import "fmt"

func main() {
    s := []struct{
        i int,    // 소문자
        b bool    // 소문자여도 아래에서 접근 가능
    }{
        {2, true},
        {3, false},
        {5, true}
    }
    fmt.Println(s)
}

Slice defaults

When slicing, you may omit the high or low bounds to use their defaults instead.

The default is zero for the low bound and the length of the slice for the high bound.

For the array

var a [10]int

these slice expressions are equivalent:

a[0:10]
a[:10]
a[0:]
a[:]

Slice length and capacity

https://go-tour-ko.appspot.com/moretypes/11

A slice has both a length and capacity.

The length of a slice the number of elements it contains. len(s) 슬라이스의 길이란 슬라이스가 포함하는 요소의 개수입니다.

The capacity of a slice is the number of elements in the underlying array, counting from the first element in the slice. cap(s) 슬라이스의 용량이란 슬라이스의 첫 번째 요소부터 계산하는 기본 배열의 요소의 개수입니다.

?? 이해 안감... ?? 뭔말이야

미리 메모리를 잡아놔서 오버헤드를 줄일 수 있음.

package main

import "fmt"

func main() {
    s := []int{2, 3, 5, 7, 11, 13}
    printSlice(s)

    // Slice the slice to give it zero length 
    s = s[:0]
    printSlice(s)

    // Extend its length.
    s = s[:4]
    printSlice(s)

    // Drop its first two values.
    s = s[2:]
    printSlice(s)

    // 왜 이건 안됨?
    s = s[5:]
    printSlice(s)
}

실행 결과

len=6 cap=6 [2 3 5 7 11 13]
len=0 cap=6 []
len=4 cap=6 [2 3 5 7]
len=2 cap=4 [5 7]
panic: runtime error: slice bounds out of range [5:2]

goroutine 1 [running]:
main.main()
    /tmp/sandbox2017825178/prog.go:21 +0xb4

Program exited: status 2.

Q. 왜 [:4] 는 cap가 안 줄어드는데 [2:] 이건 cap가 줄어들까? 그냥 .. 그런건가 ?원래? 흠.?

Nil slices

The zero value of a slice is nil.

A nil slice has a length and capacity of 0 and has no underlying array.

package main

import "fmt"

func main() {
    var s []int
    fmt.Println(s, len(s), cap(s))
    fmt.Println(s == nil)
}

실행 결과

[] 0 0
true

Creating a slice with make (make 함수로 슬라이스 만들기)

Slices can be created with the built-in make function; this is how you create dynamically-sized arrays.

The make function allocates a zeroed array and returns a slice that refers to that array:

make 함수는 0으로 이루어진 배열을 할당합니다. make([]타입, 길이, 용량)

a := make([]int, 5)    // len(a)=5

To specify a capacity, pass a third argument to make:

b := make([]int, 0, 5)    // len(b)=0, cap(b)=5

b = b[:cap(b)]    // len(b)=5, cap(b)=5
b - b[1:]        // len(b)=4, cap(b)=4
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)
}

Slices of slices

Slices can contain any type, including other slices.

package main

import (
    "fmt"
    "strings"
)

func main() {
    board := [][]string{
        []string("_", "_", "_"},
        []string("_", "_", "_"},
        []string("_", "_", "_"},
    }

    board[0][0] = "X"
    board[2][2] = "0"

    for i := 0; i < len(board); i++ {
        fmt.Printf("%s\n", strings.Join(board[i], " "))   
    }
}

Appending to a slice

https://go-tour-ko.appspot.com/moretypes/15

func append(s []T, vs ...T) []T

The first parameter s of append is a slice of type T, and the rest are T values to append to the slice.

The resulting value of append is a slice containing all the elements of the original slice plus the provided values.

If the backing array of s is too small to fit all the given values a bigger array will be allocated. The returned slice will point to the newly allocated array.

package main

import "fmt"

func main() {
    var s []int
    printSlice(s)

    // append works on nil slices.
    s = append(s, 0)
    printSlice(s)

    // The slice grows as needed.
    s = append(s, 1)
    printSlice(s)

    // We can add more than add one element at a time.
    s = append(s, 2, 3, 4)
    printSlice(s)
}

func printSlice(s []int) {
    fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}

실행 결과

len=0 cap=0 []
len=1 cap=1 [0]
len=2 cap=2 [0 1]
len=5 cap=6 [0 1 2 3 4]    // 왜 cap 이 6이 되었을까? 

Range

The range form of the for loop iterates over a slice or map.

When raging over a slice, two values are returned for each iteration. The first is the index, and the second is a copy of the element at that index.

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

Range continued

You can skip the index or value by assigning to _.

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

if you only want the index, you can omit the second variable.

for i := range pow

Exercise: Slices

'개발~' 카테고리의 다른 글

자주 사용하는 카프카 명령어  (0) 2021.11.04
IntelliJ에서 자주 쓰는 Git 명령어(feat. JIRA)  (0) 2021.05.24
vi 편집기 입력 에러  (1) 2020.05.18
Linux alias 설정하기  (0) 2020.03.20
참고 사이트  (0) 2017.03.06

댓글