Go의 슬라이스는 다른 언어의 리스트나 배열과 비슷해요. 특정 타입(또는 인터페이스)의 원소를 여러 개 담을 수 있어요.
Go의 슬라이스는 배열을 기반으로 해요. 배열은 크기가 고정되어 있어요. 반면 슬라이스는 동적으로 크기가 변하는, 배열의 원소들을 유연하게 바라보는 뷰예요.
슬라이스는 []T처럼 쓰는데, 여기서 T는 슬라이스에 담기는 원소의 타입이에요:
var empty []int // an empty slice
withData := []int{0,1,2,3,4,5} // a slice pre-filled with some data
대괄호 표기법을 사용하면 0부터 시작하는 인덱스에 있는 원소를 가져오거나 설정할 수 있어요:
withData[1] = 5
x := withData[1] // x is now 5
기존 슬라이스에서 원소 범위를 가져와 새 슬라이스를 만들 수 있어요. 이번에도 대괄호 표기법을 사용하는데, 시작 인덱스(포함)와 끝 인덱스(제외)를 모두 지정해요. 시작 인덱스를 지정하지 않으면 기본값은 0이에요. 끝 인덱스를 지정하지 않으면 기본값은 슬라이스의 길이예요.
newSlice := withData[2:4]
// => []int{2,3}
newSlice := withData[:2]
// => []int{0,1}
newSlice := withData[2:]
// => []int{2,3,4,5}
newSlice := withData[:]
// => []int{0,1,2,3,4,5}
append 함수를 사용하면 슬라이스에 원소를 추가할 수 있어요.
아래에서는 a 슬라이스에 4와 2를 추가해요.
a := []int{1, 3}
a = append(a, 4, 2)
// => []int{1,3,4,2}
append는 항상 새 슬라이스를 반환하고, 기존 슬라이스에 원소를 추가하고 싶을 때는 위에서처럼 첫 번째 인자로 넘긴 슬라이스 변수에 그 결과를 다시 할당하는 게 일반적이에요.
append로 두 슬라이스를 합칠 수도 있어요:
nextSlice := []int{100,101,102}
newSlice := append(withData, nextSlice...)
// => []int{0,1,2,3,4,5,100,101,102}
일반적으로 Go의 함수는 정해진 개수의 인자만 받아요. 하지만 Go에서는 가변 인자 함수를 작성할 수도 있어요.
가변 인자 함수는 개수가 변하는 인자를 받는 함수예요.
함수 정의에서 마지막 매개변수의 타입 앞에 줄임표 ...를 붙이면, 그 매개변수로 어떤 개수의 인자든 받을 수 있어요.
func find(a int, b ...int) {
// ...
}
위 함수에서 매개변수 b는 가변이고, b에 0개 이상의 인자를 넘길 수 있어요.
find(5, 6)
find(5, 6, 7)
find(5)
가변 매개변수는 함수의 마지막 매개변수여야 해요.
가변 인자 함수는 개수가 변하는 인자들을 가변 매개변수의 타입에 해당하는 슬라이스로 변환하는 방식으로 동작해요.
다음은 가변 인자 함수 구현 예시예요.
func find(num int, nums ...int) {
fmt.Printf("type of nums is %T\n", nums)
for i, v := range nums {
if v == num {
fmt.Println(num, "found at index", i, "in", nums)
return
}
}
fmt.Println(num, "not found in ", nums)
}
func main() {
find(89, 90, 91, 95)
// =>
// type of nums is []int
// 89 not found in [90 91 95]
find(45, 56, 67, 45, 90, 109)
// =>
// type of nums is []int
// 45 found at index 2 in [56 67 45 90 109]
find(87)
// =>
// type of nums is []int
// 87 not found in []
}
위 프로그램의 find(89, 90, 91, 95) 줄에서 find 함수에 전달되는 가변 인자는 90, 91, 95예요.
find 함수는 num 뒤에 가변 int 매개변수를 기대해요.
따라서 이 세 인자는 컴파일러에 의해 int 타입의 슬라이스인 []int{90, 91, 95}로 변환되고, 그다음 find 함수에 nums로 전달돼요.
가끔은 이미 슬라이스가 있는데 그걸 가변 인자 함수에 넘기고 싶을 때가 있어요.
슬라이스 뒤에 ...를 붙여서 넘기면 돼요.
그러면 컴파일러가 가변 인자 함수 안에서 그 슬라이스를 있는 그대로 사용하게 돼요.
이 경우에는 위에서 설명한 슬라이스 생성 단계가 그냥 생략돼요.
list := []int{1, 2, 3}
find(1, list...) // "find" defined as shown above
예비 마술사 Elyse는 기초를 좀 연습할 필요가 있어요. 그녀에게는 다루고 싶은 카드 더미가 있어요.
조금 더 쉽게 하기 위해 그녀는 1부터 10까지의 카드만 사용해요.
카드 연습을 할 때 Elyse는 카드 한 벌에서 가장 좋아하는 세 장, 즉 2, 6, 9로 시작하는 걸 좋아해요.
그 카드들을 그 순서대로 담은 슬라이스를 반환하는 FavoriteCards 함수를 작성해요.
cards := FavoriteCards()
fmt.Println(cards)
// Output: [2 6 9]
주어진 스택에서 index 위치에 있는 카드를 반환해요.
card := GetItem([]int{1, 2, 4, 1}, 2) // card == 4
인덱스가 범위를 벗어나면(즉, 음수이거나 스택의 끝을 넘어서면) -1을 반환해야 해요:
card := GetItem([]int{1, 2, 4, 1}, 10) // card == -1
Go에서는 관례상 "범위를 벗어난" 값을 반환하는 대신 오류를 반환해요.
여기서는 양의 정수가 기대될 때 "범위를 벗어난" 값이 -1이에요.
오류를 반환할 때는 오류와 함께 zero value을 반환하는 것이 관용적이라고 여겨져요.
올바른 반환 값과 함께 오류를 반환하는 방법은 앞으로의 연습 문제에서 다뤄요.
index 위치의 카드를 주어진 새 카드와 교환하고, 조정된 스택을 반환해요.
이때 입력 슬라이스가 수정되는데, 이는 기대되는 동작이에요.
index := 2
newCard := 6
cards := SetItem([]int{1, 2, 4, 1}, index, newCard)
fmt.Println(cards)
// Output: [1 2 6 1]
인덱스가 범위를 벗어나면(즉, 음수이거나 스택의 끝을 넘어서면) 새 카드를 스택의 끝에 덧붙여요:
index := -1
newCard := 6
cards := SetItem([]int{1, 2, 4, 1}, index, newCard)
fmt.Println(cards)
// Output: [1 2 4 1 6]
value 매개변수에 지정된 카드를 스택 맨 위에 추가해요.
slice := []int{3, 2, 6, 4, 8}
cards := PrependItems(slice, 5, 1)
fmt.Println(cards)
// Output: [5 1 3 2 6 4 8]
value 매개변수에 인자가 주어지지 않으면 결과는 원래 슬라이스와 같아요.
slice := []int{3, 2, 6, 4, 8}
cards := PrependItems(slice)
fmt.Println(cards)
// Output: [3 2 6 4 8]
스택에서 index 위치의 카드를 제거하고 스택을 반환해요.
이때 입력 슬라이스가 수정될 수 있는데, 괜찮아요.
cards := RemoveItem([]int{3, 2, 6, 4, 8}, 2)
fmt.Println(cards)
// Output: [3 2 4 8]
인덱스가 범위를 벗어나면(즉, 음수이거나 스택의 끝을 넘어서면) 스택을 그대로 두어야 해요:
cards := RemoveItem([]int{3, 2, 6, 4, 8}, 11)
fmt.Println(cards)
// Output: [3 2 6 4 8]
Exercism에 가입하고 Go 트랙을 개념 34개연습 문제 165개, 그리고 실제 사람의 멘토링과 함께 배우고 익혀 보세요. 모두 무료예요.