관리 메뉴

A seeker after truth

Golang 기본(3) - 클로저, 배열, 슬라이스, 맵, 패키지 본문

Programming Language/Golang(Go)

Golang 기본(3) - 클로저, 배열, 슬라이스, 맵, 패키지

dr.meteor 2019. 10. 12. 00:02

*본문은 <예제로 공부한 go 프로그래밍>(http://golang.site/go/basics) 사이트를 참고하여 공부하면서 정리한 내용입니다. Tip 위주로 간략하게 정리했으며, 보탬 내용은 <Go 디스커버리>(염재현 저, 한빛미디어, 2016)을 참고하여 작성되었습니다.

 

11.클로저 closure

함수 바깥에 있는 변수를 참조하는 함수'값'을 클로저라 일컫는데, 이때 함수는 바깥의 변수를 마치 함수 안으로 끌어들인 듯이 그 변수를 읽거나 쓸 수 있게 된다.

아래 예제에서 nextValue()함수는 int를 리턴하는 익명함수(func() int)를 리턴하는 함수이다. 이 익명함수는 함수 바깥의 변수 i를 참조하고 있다. 즉 i를 로컬변수로 갖는 것이 아니다. 이 경우 i값이 변경 가능하다는 것!

func nextValue() func() int {
	i := 0
	return func() int {
		i++
		return i
	}
}



 

12.배열

//기본 선언

var a [3]int

var a1 = [3]int{1,2,3}//초기화

var a3 = [...]int{1,2,3}//초기화 요소 숫자만큼 배열크기 정해짐

 

//다배열/차원 배열

var multiArray [3][4][5]int//정의

var b = [2][3]int{

  {1,2,3},

  {4,5,6}, //끝에 콤마 추가해야함

}



 

13.슬라이스 slice

/*배열과 달리 고정된 크기를 미리 지정하지 않을 수 있고, 차후 그 크기를 동적으로 변경할 수도 있고, 또한 부분 배열을 발췌할 수도 있다. 방금 언급된 이 기능들은 그냥 배열에는 없는 기능들이다.*/

var c []int

c = []int{1,2,3}

 

/*go의 내장함수 make() 이용 가능. 이것으로 슬라이스를 생성하면, 개발자가 슬라이스의 길이와 용량을 직접 지정 가능. 이 함수의 파라미터 안에 생성할 슬라이스 타입, length, capacity 순으로 지정하여 넣음. 또 len, cap라는 내장함수가 있어 이를 통해 앞선 파라미터의 값들을 확인할 수 있다.*/

s := make([]int, 5, 10)

println(len(s), cap(s))

 

/*슬라이스에 별도의 길이와 용량을 지정하지 않으면 기본적으로 길이와 용량이 0인 슬라이스 Nil Slice가 만들어짐. 특수하게, nil이란 것과 비교하면 true를 리턴한다고 함*/

var s []int

if s==nil {

  println("Nil Slice")

}

 

//부분 슬라이스

s1 := []int{0,1,2,3,4,5}

s1 = s1[2:5]//2,3,4

s1 = s1[1:]

s1 = s1[:4]

s1 = s1[:]

 

//슬라이스 추가: 병합과 복사

//append 함수의 첫 파라미터는 슬라이스 객체이고, 두번째 그 이하는 추가할 요소의 값이다.

s2 = []int{0,1}

s2 = append(s2,2)

s2 = append(s2,3,4,5)

 

append가 슬라이스에 새로운 데이터를 추가하는 경우, 용량(capacity)이 아직 남아 있는 경우는 그 용량 내에서 슬라이스의 length를 변경하여 데이터를 추가하고, 용량을 초과하는 경우 현재 용량의 "2배"에 해당하는 새로운 underlying array를 생성하고 "기존 배열값들을 모두 새 배열에 복제한 후 다시 슬라이스를 할당한다."

 

/*append 함수의 2개의 파라미터가 모두 슬라이스일 경우, 처음 슬라이스 뒤에 두번째 파라미터의 슬라이스를 추가하게 된다. 여기서 두번째 파라미터의 슬라이스 뒤에 ...을 붙여야 한다. 이는 해당 슬라이스의 컬렉션을 표현하고, 두번재 슬라이스의 모든 요소들의 집합을 나타낸다.*/

sliceA := []int{1,2,3}

sliceB := []int{4,5,6}

sliceA = append(sliceA, sliceB...)//[1 2 3 4 5 6]

 

//copy함수를 사용하면 한 슬라이스를 다른 슬라이스로 복제할 수도 있다.

source := []int{0,1,2}

target := make([]int, len(source), cap(source)*2)

copy(target,source)//[0 1 2]

 

slice는 배열포인터, len, cap 세 필드를 갖고 있다.



 

13. MAP

//c++ nullptr = JAVA's null = Go's Nil

/*파이썬의 dic과 같은 것이다. 즉 키에 대응되는 값을 찾는 해시테이블을 구현한 것! 이때 선언된 변수 idMap은 nil값을 가지며(map이 reference 타입이므로), 이를 Nil Map이라 부른다. Nil Map엔 어떤 데이터를 쓸 수 없는데, map을 초기화하기 위해 make()함수를 쓸 수 있다.*/

var idMap2 map[int]string //map[key타입]value타입 식으로 선언

idMap2 = make(map[int]string)

//리터럴(literal)로 map 초기화. 걍 직접 값을 쓰는 것을 말한다.

tickers := map[string]string{

  "GOOG" : "GOOGLE INC"

  "MSFT" : "MICROSOFT"

  "FB" : "FACEBOOK"

}

 

//값 추가 혹은 갱신

idMap2[901] = "apple"

idMap2[134] = "grape"

tempstr := idMap2[134]//값 읽기

noData := idMap2[999]//값 없으면 nil 혹은 zero 리턴

delete(idMap2, 777)//삭제

 

/* map에 대해 특정 키가 존재하는지 체크하는 기능이 있다. "map변수[키]" 읽기를 수행하면 키에 대응되는 값 & 그 키의 존재 여부를 나타내는 bool값 2가지를 리턴한다. */

val, exists := tickers["MSFT"]

if !exists {

  println("No MSFT ticker")

}

 

/* map이 갖고 있는 모든 요소들을 출력하려면 for range 루프를 이용하면 된다.

그러면 map 키와 값 2개의 데이터를 리턴한다. 단, 출력 시 map은 unordered이므로 무작위로 출력된다.*/

myMap := map[string]string{

  "A" : "Apple"

  "B" : "Banana"

  "C" : "Charlie"

}

for key, val := range myMap {

  fmt.Println(key, val)

}



 

14. 패키지

1) main 패키지: 일반적으로 패키지는 라이브러리로서 사용되지만, main으로 명명된 패키지의 경우 컴파일러가 공유 라이브러리가 아닌 실행(executable) 프로그램으로 만든다. 그리고 이 main 패키지 안의 main() 함수가 프로그램의 시작점 즉 entry point가 된다.

2) 패키지의 scope가 존재한다. 패키지 내 함수,구조체,인터페이스,메서드의 이름 첫문자를 대문자로 시작하면 이는 public, 즉 패키지 외부에서 호출 및 사용할 수 있는 것이다. 소문자로 시작하면 패키지 내부에서만 사용될 수 있다.

3) init()함수는 패키지 실행 시 처음으로 호출되는 함수이다. 패키지가 로드되면 별도의 호출 없이 자동으로 호출된다.

 

package testlib

var pop map[string]string

func init() {

  pop = make(map[string]string)//패키지 로드시 map 초기화

}

//패키지 안의 init함수만을 호출하고자 하면 패키지 import시 _란 alias를 지정한다.

package main

import _ "other/xlib"//other/xlib란 패키지를 호출하며 _alias를 지정.

 

//종합 예제문

package testlib

import "fmt"

 

var pop map[string]string

 

func init() {

  pop = make(map[string]string)

  pop["Adele"] = "Hello"

}

 

func GetMusic(singer string) string { //외부서 호출 가능

  return pop[singer]

}

 

func getKeys() {

  for _, kv := range pop {

    fmt.Println(kv)

  }

}

 

사이즈가 큰 복잡한 라이브러리의 경우 "go install" 명령을 사용해 라이브러리를 컴파일하여 cache할 수 있다. 이렇게 하면 다음 빌드 시 빌드타임을 크게 줄일 수 있다.