Backend with Golang

Go's Syntax

아직개구리 2023. 4. 27. 19:09

참고 자료 

1. https://go.dev/blog/declaration-syntax

2. https://go.dev/tour/basics/12

3. https://go.dev/tour/moretypes/1

4. https://soyoja.com/263 : 비트 이동 연산자 


Go 와 C syntax가 다른 이유에 대해서 

C syntax는 declare할 때 그것이 앞으로 가지게 될 type을 명시하는 형태이다. int x; 와 같이 x는 int 일 것임을 declare하는 것이다. 

int *p;  *p가 int라는 것. p가 int를 향한 pointer라는 것을 의미한다. 

int a[2]; a[2]가 int type을 가지기 때문에 a는 int의 array임을 의미한다.

함수의 경우에는 

paren 바깥에서 arguments 의 타입들을 명시했지만, modern notation에서는 아래와 같이 명시한다. 

int main(int argc, char *argv[]) { /* ... */ }

이런식으로 명시하는 경우 빠르게 혼란스러워진다. 유명한 예시는 function pointer가 있다. 읽어서 해독하기가 어려워 진다. 

 

Go syntax의 경우 이제 x int / p *int / a [3]int 이런식으로 선언이된다. left->right방향으로 읽히기 때문에 더 읽기 쉬워진다. 

또한 type과 expression syntax 사이의 구분이 closure를 쓰고, 부르기를 쉽게 할 수 있다. 

sum := func(a, b int) int { return a+b } (3, 4)

Go의 type syntax 는 bracket을 타입의 왼쪽에 놓는다. Go의 pointer 는 C처럼 * 를 쓰는데, *를 앞쪽에 쓰는 이유는 multiplication과 헷갈리기 때문. 


Variable 관련 

var statement는 한개 이상의 variables를 선언할 수 있다. function arguments에서 x int, y int 를 x, y int로 줄여 쓸 수 있는 것처럼 여러개를 선언하고 type을 마지막에 쓴다. 

만약에 vaiable 에 initializer가 있다면, type을 쓰지 않아도 된다. initializer의 type을 가지게 된다. 

 

Short variable declarations 

함수 안에서 := 를 사용해서 var 대신 사용 할 수 있다. 함수 밖에서는 keyword(var, func, 등) 을 사용해야 가능하다 .

:= 는 결국 right hand side가 typed가 되면 새로운 variable이 같은 타입을 가지게 되는 것이다. 만약에 untyped numeric constant가 오른쪽에 오면, constant의 precision을 사용해서 결정된다.

 

Zero value : numeric이면 0, boolean이면 false, string이면 ""

 

Type conversions: T(v) 는 v를 type T로바꿔준다. C와 다르게, Go는 다른 type을 가지는 경우 explicit conversion을 필요로 한다. float value를 var z uint = f 이런식으로 automatic conversion을 제공하지 않는다는 것. 

 

constants 같은 경우는 := syntax를 사용해서 불가능하고, variable 선언하는 것처럼 가능하다. 


More Types 

*T는 T의 pointer이다. zero value는 nil이다. 

var p *int

*operator를 사용해서 pointer의 underlying value를 나타낸다.

i:=42; p=&i; &operator는 pointer를 generate한다. 

*p = 21 이런식으로 p를 통해서 i의 값을 바꿀수도 있다. C와 다르게 pointer arithmetic을 지원하지 않음. 

 

Pointer to struct : struct field 또한 pointer로 접근 가능하다. 주소를 통해 값에 접근하는 것을 dereference, 역참조라고 한다. 보통 이제 값에 접근하기 위해서는 (*p).X로 접근할 수 있지만, explicit dereference 없이도 가능하도록 허용한다. 

v := Vertex{1,2} // 이 때 subset of fields만 사용해도 된다. Vertex{X: 1} -> Y:0 is implicit
p := &v
p.X = 1e3

Arrays : array의 길이가 array 타입의 일부이기 때문에 resized될 수 없다. 

var a [2]string // 2개의 string value들을 가진 array
primes := [6]int{2,3,5,7,11,13}

Slices: array는 fixed size를 가지고 있지만, slice는 dynamically-sized된다. array를 참조하는데, flexible view를 제공하는 것과 같다. reference와 비슷해서, 아무 data를 저장하지 않고, 값을 바꾸면 해당 underlying array의 값이 바뀌고, 마찬가지로 같은 aray를 참조하는 slice도 그 변화를 볼 수 있다. 아래와 같이 정의한다면, 이는 결국 [3]bool{true, true, false}를 만들고 그것을 참조하는 slice를 만드는 것과 같다. 

[]bool{true, true, false}

slice 의 zero value는 nil 이다. var s []int 일 때, s == nil true이다. built-in make 함수를 사용해서  slice를 만들 수 있다. 

slice의 capacity는 slice의 첫번째 element 부터 시작해서 underlying arry의 element 갯수이다. len(s)는 slice안에 들어있는 element 개수 이다. capacity가 충분하다면 reslice를 해서 slice의 길이를 extend할 수 있다. extend할 때 s[:늘리고자 하는 길이] 를 사용하면 된다. 

a := make([]int, 5) // len(a): 5, cap(a): 5
 
board := [][]string{
    []string{"_", "_", "_"},
    []string{"_", "_", "_"},
    []string{"_", "_", "_"},
} // 2d array

Appending to a slice: s = append(s, 1) -> 1을 뒤에 붙이게 됨. 

Range: two values are returned for each iteration. index만 원한다면 for i := range pow, 를 사용하면 된다. pow는 여기서 slice. 

Maps : zero value of a map 은 nil이다. 

var m map[string]Vertex 
m = make(map[string]Vertex) //make function returns map of the given type, initialized and ready to use
var m = map[string]Vertex{
	"Bell Labs": {3.4, 2.3},
	"Google": {3.2, 2.3}, 
}

Mutating Maps

m[key] = elem // insert or update an element
elem = m[key] //retrieve
delete(m, key) // delete
elem, ok = m[key] //if key is in m, ok is true

이 떄, elem, ok가 이미 declared되어 있지 않다면 short declaration form 으로 선언을 할 수 있다.  

예제: https://go.dev/tour/moretypes/23

 

Function 

- Functions 또한 value이다. 다른 value처럼 전달이 가능하다. 

- Function closures:  closure 는  body밖에 있는 variable을 참조하는  fucntion value를 의미한다. function이 그 변수에 bind되어있다고 볼 수 있다. 

Function 을 variable 처럼 사용할 때 아래처럼 사용 가능하다. 

f := func(x, y float64) float64 { 
	return math.Sqrt(x*x + y*y)
}

 


갑자기 비트연산자 관련 공부 

<< n은 n만큼 왼쪽으로, >> n 은 n비트 만큼 오른쪽으로 비트를 이동한다. 

int n = 1;

n = << 1; 이면 1만큼 왼쪽으로 이동하니까 2가 된다. 

ex1) int n = 1; n=<<31; -> n : 2^32

ex2) n =<<32; 는  n=<<0 과 같으므로 1

ex3) n =<< 33; 는 n=<<1 과 같으니까 2

ex4) 32 bit -> -2 를 =<<31 하면 좌측이동하고 0으로 채운다. 이때 << 는 피 연산자가 양수나 음수인 경우 모두 0으로 자리를 채우는 반면, >> 는 양수일 경우에는 0으로 채우고, 음수이면 1로 채운다. 

>>, << 모두 아래 5비트에 해당하는 32 까지만 이동한다. 33이면 1과 같아지는 원리.

 

 

'Backend with Golang' 카테고리의 다른 글

MySQL - Transaction  (0) 2023.05.09
Docker-compose.yml 파일 작성하기 (TBU)  (0) 2023.05.09
Go: JSON  (0) 2023.05.03
Goroutine  (0) 2023.05.01
Docker 기본 개념  (1) 2023.04.26