[자료구조][Go] Stack

five·2022년 11월 9일
1
post-thumbnail

최대한 TDD에 입각해서 코드를 작성해 보았다.

코드 작성

├── collections
│   └── stack
└── pkg
    └── mod
        └── cache

코드 작성하기 앞서 프로젝트 디렉터리 안에서 go mod init collections 명령어를 셸에 입력해 모듈을 만들었다.
go mod init 명령어는 현재 디렉터리에 루트를 둔 새 모듈을 만드는 명령어로 성공적으로 실행되었다면 go.mod파일이 생성된다.

Go에서는 테스트 패키지가 기본적으로 내장되어 있어서 간편하게 사용할 수 있다.
테스트를 실행하기 위해서는 몇가지 규칙이 있는데 다음과 같다.

  • 파일명을 *_test.go로 지을 것.
  • 테스트 함수명을 func TestXxx(t *testing.T)의 형태로 지을 것.

이것만 지킨다면 어렵지 않게 테스트를 실행할 수 있다.

참고로 조금 더 괜찮은 unit test를 위해 Testify라는 툴이 사용되는데 그건 다음에 기회에 사용해 보도록 하자.

구현해야할 기능

  • push: 요소 추가
  • pop : 요소 삭제
  • peek: 최근 요소 검색
  • size: 크기 확인
  • Empty : 비어있는지 확인
  • IsNotEmpty : !Empty 보다 가독성을 높여 준다

stack은 LIFO형식의 자료구조이다.기본적으로 요소를 추가할수 있어야 하며 요소를 삭제할때에는 최근에 추가한 요소순으로 제거 해야한다.따라서 요소를 추가하는 기능,제거하는 기능 거기다 최근 추가한 요소를 보여주는 기능을 구현하고 stack의 상태를 알 수 있는 기능을 구현할 예정이다.

생성자 구현

Stack_test.go

func TestNewStack(t *testing.T) {
	s := NewStack[int]()
	
	if reflect.TypeOf(s) != reflect.TypeOf(&stack[int]{}) {
		t.Errorf("NewStack error")
	}
}

실패하는 테스트를 먼저 만든다. Go 언어는 구조체의 생성자가 없으므로 구조체를 초기화 하려면 함수를 정의한다. 이때 적당한 함수명을 생각해야 하는데 일단 New구조체이름()이라고 짓는다

Stack.go


type Stack[T any] struct {
	nodes []T
    index int
}

// NewStack stack stack 생성
func NewStack[T any]() *Stack[T] {
	return &Stack[T]{nodes: make([]T, 0)}
}

Go에서는 OOP를 직접적으로 지원해주지 않는다. 그러나 Go언어만의 독특한 방법으로 OOP를 구현할 수 있다.

push 구현

Stack_test.go

func TestStack_Push(t *testing.T) {
	s := NewStack[int]()
	s.Push(1)

	if len(s.nodes) != 1 {
		t.Errorf("push error")
	}
}

Stack.go

// Push 요소 추가
func (s *Stack[T]) Push(item T) {
	s.nodes = append(s.nodes, item)
}

pop 구현

Stack_test.go

func TestStack_Pop(t *testing.T) {
	s := NewStack[int]()
	s.Push(1)
	result := s.Pop()
	if result != 1 {
		t.Errorf("Pop error")
	}
}

Stack.go

// Pop 요소 삭제
func (s *Stack[T]) Pop() T {
	result := s.nodes[len(s.nodes)-1]
	s.nodes = s.nodes[:len(s.nodes)-1]
	return result
}

peek 구현

Stack_test.go

func TestStack_Push(t *testing.T) {
	s := NewStack[int]()
	s.Push(1)

	if len(s.nodes) != 1 {
		t.Errorf("push error")
	}
}

Stack.go

// 맨 위 요소 확인
func (s *Stack[T]) Peek() T {
	return s.nodes[len(s.nodes)-1]
}

Empty 구현

Stack_test.go

func TestStack_Empty(t *testing.T) {
	s := NewStack[int]()
	s.Push(1)

	if s.Empty() {
		t.Errorf("Empty error")
	}
}

Stack.go

// Empty 비어 있는지 확인
func (s *Stack[T]) Empty() bool {
	return len(s.nodes) == 0
}

size 구현

Stack_test.go

func TestStack_size(t *testing.T) {
	s := NewStack[int]()
	s.Push(1)
	s.Push(1)
	s.Push(1)
	s.Push(1)

	if s.size() != 4 {
		t.Errorf("size error")
	}
}

Stack.go

// Size 크기 
func (s *Stack[T]) Size() int {
	return s.index
}

적용해보기

10828. 스택

import (
	"bufio"
	"fmt"
	"os"
)

// push X: 정수 X를 스택에 넣는 연산이다.
// pop: 스택에서 가장 위에 있는 정수를 빼고, 그 수를 출력한다. 만약 스택에 들어있는 정수가 없는 경우에는 -1을 출력한다.
// size: 스택에 들어있는 정수의 개수를 출력한다.
// empty: 스택이 비어있으면 1, 아니면 0을 출력한다.
// top: 스택의 가장 위에 있는 정수를 출력한다. 만약 스택에 들어있는 정수가 없는 경우에는 -1을 출력한다.
var (
	r = bufio.NewReader(os.Stdin)
	w = bufio.NewWriter(os.Stdout)
)

func main() {
	defer w.Flush()
	var n int
	var stack = stack.NewStack[int]()
	fmt.Fscanln(r, &n)
	for i := 0; i < n; i++ {
		var command string
		fmt.Fscan(r, &command)
		switch command {
		case "push":
			var num int
			fmt.Fscan(r, &num)
			stack.Push(num)
		case "pop":
			if stack.Empty() {
				fmt.Fprintln(w, -1)
			} else {
				fmt.Fprintln(w, stack.Pop())
			}
		case "size":
			fmt.Fprintln(w, stack.Size())
		case "empty":
			if stack.Empty() {
				fmt.Fprintln(w, 1)
			} else {
				fmt.Fprintln(w, 0)
			}
		case "top":
			if stack.Empty() {
				fmt.Fprintln(w, -1)
			} else {
				fmt.Fprintln(w, stack.Peek())
			}
		}

	}
}

최종 코드

Stack_test.go

package stack

type Stack[T any] struct {
	nodes []T
}

// NewStack stack stack 생성
func NewStack[T any]() *Stack[T] {
	return &Stack[T]{nodes: make([]T, 0)}
}

// Push 요소 추가
func (s *Stack[T]) Push(item T) {
	s.nodes = append(s.nodes, item)
}

// Empty 비어 있는지 확인
func (s *Stack[T]) Empty() bool {
	return len(s.nodes) == 0
}

// IsNotEmpty 저장된 요소가 있는지 확인
func (s *Stack[T]) IsNotEmpty() bool {
	return len(s.nodes) != 0
}

// Pop 요소 삭제
func (s *Stack[T]) Pop() T {
	result := s.nodes[len(s.nodes)-1]
	s.nodes = s.nodes[:len(s.nodes)-1]
	return result
}

// 맨 위 요소 확인
func (s *Stack[T]) peek() T {
	return s.nodes[len(s.nodes)-1]
}

Stack.go

package stack

import "testing"

func TestStack_Push(t *testing.T) {
	s := NewStack[int]()
	s.Push(1)

	if len(s.nodes) != 1 {
		t.Errorf("push error")
	}
}

func TestStack_Empty(t *testing.T) {
	s := NewStack[int]()
	s.Push(1)

	if s.Empty() {
		t.Errorf("Empty error")
	}
}

func TestStack_Pop(t *testing.T) {
	s := NewStack[int]()
	s.Push(1)
	result := s.Pop()
	if result != 1 {
		t.Errorf("Pop error")
	}
}

0개의 댓글