관리 메뉴

HAMA 블로그

[이더리움으로 배우는 GO언어] 자료구조 & 컬렉션 본문

Go

[이더리움으로 배우는 GO언어] 자료구조 & 컬렉션

[하마] 이승현 (wowlsh93@gmail.com) 2019. 1. 15. 13:14

[이더리움으로 배우는 GO언어]  자료구조 & 컬렉션 

이번 글에서는 사실 이더리움하고 크게 상관없이 go 자료구조 기본에 대해서 알아 봅니다.

Array 

var a [5]int  // [0 0 0 0 0]
a[4] = 100 // [0 0 0 0 100]

5개의 비어있는 배열 생성과 값 삽입 

b := [5]int{1, 2, 3, 4, 5} // [1 2 3 4 5]

5개 요소가 들어있는 배열 생성 

a2 := [...]string{"USA", "China", "India", "Germany", "France"}
b2 := a2 // a copy of a is assigned to b
b2[0] = "Singapore"
fmt.Println("a is ", a2) // [USA China India Germany France]
fmt.Println("b is ", b2) // [Singapore China India Germany France]

5개 요소가 들어있는 배열 생성과 배열복사시 깊은 복사 이루어 지는 모습 

a3 := [...]float64{67.7, 89.8, 21, 78}
for i := 0; i < len(a3); i++ {
fmt.Printf("%d th element of a is %.2f\n", i, a[i])
}

sum := float64(0)
for i, v := range a3 {// i는 인덱스, v 는 값
fmt.Printf("%d the element of a is %.2f\n", i, v)
sum += v
}

순회를 도는 2가지 방식 

Slice

a4 := [5]int{76, 77, 78, 79, 80}
var b4 []int = a4[1:4] //creates a slice from a[1] to a[3]
b4[0] = 100
fmt.Println(a4) // [76 100 78 79 80]

배열을 가지고 슬라이스 생성 / 그렇게 만들어진 슬라이스는 배열과 레퍼런스로 연결되어 있다. 

i := make([]int, 5, 5)

make 를 이용한 슬라이스 생성 

cars := []string{"Ferrari", "Honda", "Ford"}
cars = append(cars, "Toyota") // [Ferrari Honda Ford Toyota]

append를 이용하여 슬라이스 뒤에 요소 추가 

cars := []string{"Ferrari", "Honda", "Ford"}
cars = append(cars[:0], "Toyota","KIA") // [Toyota KIA]
fmt.Println(cars)

append를 이용하여 새로운 슬라이스 생성. 

veggies := []string{"potatoes","tomatoes","brinjal"}
fruits := []string{"oranges","apples"}
food := append(veggies, fruits...)

append를 이용하여 슬라이스 뒤에 슬라이스 추가 ( 추가될 슬라이스 변수에 ... 를 추가한다) 

countries := []string{"USA", "Singapore", "Germany", "India", "Australia"}
neededCountries := countries[:len(countries)-2]
countriesCpy := make([]string, len(neededCountries))
countriesCpy2 := countries[:len(countries)-2]
copy(countriesCpy, neededCountries) //copies neededCountries to countriesCpy
countriesCpy[0] = "korea"
countriesCpy2[1] = "japan"

fmt.Println(countriesCpy) // 깊은복사 [korea Singapore Germany]
fmt.Println(countriesCpy2) // 얖은복사 [USA japan Germany]
fmt.Println(countries) // [USA japan Germany India Australia]

슬라이스의 일부분을 복사하는데 make로 만든 배열에 copy를 이용하여 복사하면 깊은 복사가 된다.

startTasks := func(ts []task) (rest []task) {
i := 0
for ; len(runningTasks) < maxActiveDialTasks && i < len(ts); i++ {
t := ts[i]
...
runningTasks = append(runningTasks, t)
}
return ts[i:]
}

현재 실행되고 있는 업무(runningTasks)가 정해진 숫자(maxActiveDialTasks) 를 넘지 않아서 대기중인 업무를 할 수 있는 상태일때,  매개변수로 받은 대기중인 업무들을 실행한다. 추가 실행한 업무는 runningTasks 에 append를 통해서 실행중인 업무로 추가되고, 실행업무에 추가되지 못한 업무들은 ts[i:] 로 리턴된다. 

scheduleTasks := func() {
queuedTasks = append(queuedTasks[:0], startTasks(queuedTasks)...)

if len(runningTasks) < maxActiveDialTasks {
nt := dialstate.newTasks(len(runningTasks)+len(queuedTasks), peers, time.Now())
queuedTasks = append(queuedTasks, startTasks(nt)...)
}
}

대기업무중인 업무들을 startTasks로 보내서 실행하게 하고, 실행이 안된 업무를 queuedTasks[:0] 같이 append 하여 다시 구성한다. 실행중인 업무가 maxActiveDialTasks 작을경우 새로운 업무들을 만들어서 (newTasks) startTasks로 실행한후에 남은 태스크는  queuedTasks 에 append한다.

Map

m := make(map[string]int)
m["first"] = 1
fmt.Println(m["first"]) // 1

키가 string 값이 int 은 맵을 make 를 통해 만들어서 추가/출력 한다.

var fileExtensions = map[string]string{
"Python": ".py",
"C++": ".cpp",
"Java": ".java",
"Golang": ".go",
"Kotlin": ".kt",
}
fmt.Println(fileExtensions) // map[Python:.py C++:.cpp Java:.java Golang:.go Kotlin:.kt]
delete(fileExtensions, "Kotlin")
delete(fileExtensions, "Javascript")
fmt.Println(fileExtensions) // map[Python:.py C++:.cpp Java:.java Golang:.go]

 delete 를 통해서 삭제한다.

s := map[int]string{5: "five", 2: "second"}
_, ok := s[5] // check for existence // ok true
_, ok2 := s[6] // check for existence // ok false

 키가 없을 경우에는 ok 가 false

var m1 = map[string]int{
"one": 1,
"two": 2,
"three": 3,
"four": 4,
"five": 5,
}

var m2 = m1
fmt.Println("m1 = ", m1) // map[one:1 two:2 three:3 four:4 five:5]
fmt.Println("m2 = ", m2) // map[one:1 two:2 three:3 four:4 five:5]

m2["ten"] = 10
fmt.Println("m1 = ", m1) // map[ten:10 one:1 two:2 three:3 four:4 five:5]
fmt.Println("m2 = ", m2) // map[ten:10 one:1 two:2 three:3 four:4 five:5]

맵끼리의 대입은 레퍼런스식으로 공유한다.

var personAge = map[string]int{
"Rajeev": 25,
"James": 32,
"Sarah": 29,
}

for name, age := range personAge {
fmt.Println(name, age)
}

 맵을 순회하는 방식은 range를 사용하여 key와 value를 추출한다.


MapSet("github.com/deckarep/golang-set")
이 자료구조는 도커,이더리움,쿠버네이트에서 사용되는 간단한 set 타입이다.

s := mapset.NewSet()
s.Add("Cooking")
s.Add("English")
s.Add("Math")
s.Add("Biology")
s.Add("Biology")
s.Add("Biology")
s.Add("Biology")
fmt.Printf(s.String()) // Set{Biology, Cooking, English, Math}

중복적재가 안되는 모습이구요~

s.Pop() // 아무거나 랜덤으로 하나 제거 합니다.

scienceSlice := []interface{}{"Biology", "Chemistry","Biology"}
scienceClasses := mapset.NewSetFromSlice(scienceSlice)
fmt.Printf(scienceClasses.String()) //Set{Biology, Chemistry}

슬라이스로부터 셋을 만드는 모습이구요~ 덤으로 중복요소가 없어졌습니다.

requiredClasses := mapset.NewSet()
requiredClasses.Add("Biology")
requiredClasses.Add("Cooking")

scienceSlice := []interface{}{"Biology", "Chemistry"}
scienceClasses := mapset.NewSetFromSlice(scienceSlice)

electiveClasses := mapset.NewSet()
electiveClasses.Add("Welding")
electiveClasses.Add("Music")

bonusClasses := mapset.NewSet()
bonusClasses.Add("Go Programming")
bonusClasses.Add("Python Programming")

allClasses := requiredClasses.Union(scienceClasses).Union(electiveClasses).Union(bonusClasses)
fmt.Println(allClasses) //Set{Python Programming, Biology, Cooking, Chemistry, Welding, Music, Go Programming}

Set 을 Union으로 합쳤습니다. 역시 중복요소는 제거됩니다.

//포함여부 검사?
fmt.Println(scienceClasses.Contains("Cooking")) //false

//과학클래스 과목을 제외한 모든 클래스는?
fmt.Println(allClasses.Difference(scienceClasses)) //Set{Music, Go Programming, Python Programming, Cooking, Welding}

//교집합
fmt.Println(scienceClasses.Intersect(requiredClasses)) //Set{Biology}

//How many bonus classes do you offer?
fmt.Println(bonusClasses.Cardinality()) //2

 Contains 로 포함여부 검사/Difference 로 포함그룹군 제외/ Intersect로 교집합추출/ Cardinality로 요소갯수

container/List

values := list.New()
// Add 3 elements to the list.
values.PushBack("bird")
values.PushBack("cat")
values.PushFront("snake")
// Add 100 elements at the front.
for i := 0; i < 20; i++ {
// Convert ints to strings.
values.PushFront(strconv.Itoa(i))
}

// Loop over container list.
for temp := values.Front(); temp != nil; temp = temp.Next() {
fmt.Println(temp.Value)
}

앞,뒤로 삽입할수 있는 리스트 

container/heap


// An Item is something we manage in a priority queue.
type Item struct {
value string // The value of the item; arbitrary.
priority int // The priority of the item in the queue.
// The index is needed by update and is maintained by the heap.Interface methods.
index int // The index of the item in the heap.
}

// A PriorityQueue implements heap.Interface and holds Items.
type PriorityQueue []*Item

func (pq PriorityQueue) Len() int { return len(pq) }

func (pq PriorityQueue) Less(i, j int) bool {
// We want Pop to give us the highest, not lowest, priority so we use greater than here.
return pq[i].priority > pq[j].priority
}

func (pq PriorityQueue) Swap(i, j int) {
pq[i], pq[j] = pq[j], pq[i]
pq[i].index = i
pq[j].index = j
}

func (pq *PriorityQueue) Push(x interface{}) {
n := len(*pq)
item := x.(*Item)
item.index = n
*pq = append(*pq, item)
}

func (pq *PriorityQueue) Pop() interface{} {
old := *pq
n := len(old)
item := old[n-1]
item.index = -1 // for safety
*pq = old[0 : n-1]
return item
}

// update modifies the priority and value of an Item in the queue.
func (pq *PriorityQueue) update(item *Item, value string, priority int) {
item.value = value
item.priority = priority
heap.Fix(pq, item.index)
}

// This example creates a PriorityQueue with some items, adds and manipulates an item,
// and then removes the items in priority order.
func main() {
// Some items and their priorities.
items := map[string]int{
"banana": 3, "apple": 2, "pear": 4,
}

// Create a priority queue, put the items in it, and
// establish the priority queue (heap) invariants.
pq := make(PriorityQueue, len(items))
i := 0
for value, priority := range items {
pq[i] = &Item{
value: value,
priority: priority,
index: i,
}
i++
}
heap.Init(&pq)

// Insert a new item and then modify its priority.
item := &Item{
value: "orange",
priority: 1,
}
heap.Push(&pq, item)
pq.update(item, item.value, 5)

// Take the items out; they arrive in decreasing priority order.
for pq.Len() > 0 {
item := heap.Pop(&pq).(*Item)
fmt.Printf("%.2d:%s ", item.priority, item.value)
// 05:orange 04:pear 03:banana 02:apple
}
}

heap을 이용해서 우선순위큐를 구현한 모습이구요~

container/ring

package main

import (
"container/ring"
"fmt"
"time"
)

func main() {
number := []string{"1", "2", "3", "4", "5"}

// 버퍼 생성
r := ring.New(len(number)) // 버퍼에 값 채움
for i := 0; i < r.Len(); i++ {
r.Value = number[i]
r = r.Next()
}
// 버퍼 순회하며 출력
r.Do(func(x interface{}) {
fmt.Println(x)
})


        // 버퍼 무한 순회하며 출력
for _ = range time.Tick(time.Second * 1) {
fmt.Println(r.Value)
r = r.Next()
}
}

Do를 통해서 한바퀴 순회하고, Next()를 통해서 무한이 순회할 수 있는 환영큐입니다.

Comments