list.New()로 새로운 인스턴스를 만들어 PushBack, PushFront, InsertBefore, InsertAfter함수를 사용하여, 리스트의 여러위치에 요소를 삽입할수있다.
Front(),Next()함수를 사용하여 각 요소별 순회가 가능하다. e는 List의 pointer변수이다.
//ch22/ex22.1/ex22.1.go
package main
import (
"container/list"
"fmt"
)
func main() {
v := list.New() // ❶ 새로운 리스트 생성
e4 := v.PushBack(4) // ❷ 리스트 뒤에 요소 추가
e1 := v.PushFront(1) // ❸ 리스트 앞에 요소 추가
v.InsertBefore(3, e4) // ❹ e4 요소 앞에 요소 삽입
v.InsertAfter(2, e1) // ➎ e1 요소 뒤에 요소 삽입
for e := v.Front(); e != nil; e = e.Next() { // ➏ 각 요소 순회
fmt.Print(e.Value, " ")
}
fmt.Println()
for e := v.Back(); e != nil; e = e.Prev() { // ➐ 각 요소 역순 순회
fmt.Print(e.Value, " ")
}
}
아래는 배열,슬라이스와 리스트의 Big-O 표기법이다.
리스트를 사용하여 Queue를 구현할수있다.
//ch22/ex22.2/ex22.2.go
package main
import (
"container/list"
"fmt"
)
type Queue struct { // ❶ Queue 구조체 정의
v *list.List
}
func (q *Queue) Push(val interface{}) { // ❷ 요소 추가
q.v.PushBack(val)
}
func (q *Queue) Pop() interface{} { // ❸ 요소을 반환하면서 삭제
front := q.v.Front()
if front != nil {
return q.v.Remove(front)
}
return nil
}
func NewQueue() *Queue {
return &Queue{list.New()}
}
func main() {
queue := NewQueue() // ❹ 새로운 큐 생성
for i := 1; i < 5; i++ { // ➎ 요소 입력
queue.Push(i)
}
v := queue.Pop()
for v != nil { // ➏ 요소 출력
fmt.Printf("%v -> ", v)
v = queue.Pop()
}
}
아래소스는 리스트를 사용하여 스택을 만드는 예제이다.
//ch22/ex22.3/ex22.3.go
package main
import (
"container/list"
"fmt"
)
type Stack struct {
v *list.List
}
func NewStack() *Stack {
return &Stack{list.New()}
}
func (s *Stack) Push(val interface{}) {
s.v.PushBack(val) // ❶ 맨 뒤에 요소 추가
}
func (s *Stack) Pop() interface{} {
back := s.v.Back() // ❷ 맨 뒤에서 요소를 반환
if back != nil {
return s.v.Remove(back)
}
return nil
}
func main() {
stack := NewStack()
for i := 1; i < 5; i++ {
stack.Push(i)
}
val := stack.Pop()
for val != nil {
fmt.Printf("%v -> ", val)
val = stack.Pop()
}
}
아래의 소스를 리스트를 사용하여 링 자료구조를 구현하였다.
//ch22/ex22.4/ex22.4.go
package main
import (
"container/ring"
"fmt"
)
func main() {
r := ring.New(5) // ❶ 요소가 5개인 링 생성
n := r.Len() // ❷ 링 개수 반환
for i := 0; i < n; i++ {
r.Value = 'A' + i // ❸ 순회하면 모든 요소에 값 대입
r = r.Next()
}
for j := 0; j < n; j++ {
fmt.Printf("%c ", r.Value) // ❹ 순회하며 값 출력
r = r.Next()
}
fmt.Println() // 한줄 띄우기
for j := 0; j < n; j++ {
fmt.Printf("%c ", r.Value) // ➎ 순회하며 값 출력
r = r.Prev()
}
}
//ch22/ex22.6/ex22.6.go
package main
import "fmt"
type Product struct {
Name string
Price int
}
func main() {
m := make(map[int]Product) // ❶ 맵 생성
m[16] = Product{"볼펜", 500}
m[46] = Product{"지우개", 200}
m[78] = Product{"자", 1000}
m[345] = Product{"샤프", 3000}
m[897] = Product{"샤프심", 500}
for k, v := range m { // ❸ 맵 순회
fmt.Println(k, v)
}
}
맵에서 아래 소스와 같이 값을 삭제할수도있다.
//ch22/ex22.7/ex22.7.go
package main
import "fmt"
func main() {
m := make(map[int]int) // ❶ 맵 생성
m[1] = 0 // ❷ 요소 추가
m[2] = 2
m[3] = 3
delete(m, 3) // ❸ 요소 삭제
delete(m, 4) // ❹ 없는 요소 삭제 시도
fmt.Println(m[3]) // ➎ 삭제된 요소 값 출력
fmt.Println(m[1]) // ➏ 존재하는 요소 값 출력
}
아래는 간단한 해시함수를 사용하여 맵을 만드는 예제이다.
만약에 m[hash(33)] = 50 으로 값을 저장한다면 hash(23)과 충돌한다. 이런경우 가장 단순한 해결방법은 인덱스 위치마다 값이 아니라 리스트를 저장하게 하면 해결할수있다.
//ch22/ex22.8/ex22.8.go
package main
import "fmt"
const M = 10 // ❶ 나머지 연산의 분모
func hash(d int) int {
return d % M // ❷ 나머지 연산
}
func main() {
m := [M]int{} // ❸ 값을 저장할 배열 생성
m[hash(23)] = 10 // ❹ 키 23에 값 설정
m[hash(259)] = 50 // ➎ 키 259에 값 설정
fmt.Printf("%d = %d\n", 23, m[hash(23)])
fmt.Printf("%d = %d\n", 259, m[hash(259)])
}
23장 에러핸들링
WriteFile()함수는 파일에 string을 저장하고, ReadFile()함수는 data.txt파일의 내용을 읽어서 반환하는 함수이다.
//ch23/ex23.1/ex23.1.go
package main
import (
"bufio"
"fmt"
"os"
)
func ReadFile(filename string) (string, error) {
file, err := os.Open(filename) // ❶ 파일 열기
if err != nil {
return "", err // ❷ 에러 나면 에러 반환
}
defer file.Close() // ❸ 함수 종료 직전 파일 닫기
rd := bufio.NewReader(file) // ❹ 파일 내용 읽기
line, _ := rd.ReadString('\n')
return line, nil
}
func WriteFile(filename string, line string) error {
file, err := os.Create(filename) // ➎ 파일 생성
if err != nil { // ➏ 에러 나면 에러 반환
return err
}
defer file.Close()
_, err = fmt.Fprintln(file, line) // ➐ 파일에 문자열 쓰기
return err
}
const filename string = "data.txt"
func main() {
line, err := ReadFile(filename) // ➑ 파일 읽기 시도
if err != nil {
err = WriteFile(filename, "This is WriteFile") // ➒ 파일 생성
if err != nil { // ❿ 에러를 처리
fmt.Println("파일 생성에 실패했습니다.", err)
return
}
line, err = ReadFile(filename) // ⓫ 다시 읽기 시도
if err != nil {
fmt.Println("파일 읽기에 실패했습니다.", err)
return
}
}
fmt.Println("파일내용:", line) // ⓬ 파일 내용 출력
}
위의 소스는 함수에서 발생한 에러를 처리하였고, 아래의 소스는 직접 에러를 만들어서 반환하는 함수를 만들어보겠다.