Tucker의 Go 언어 프로그래밍 책과 유튜브를 통해 학습 중입니다.
* 흔히 하는 실수
package main
import "fmt"
func addNum(slice []int) {
slice = append(slice, 4)
}
func main() {
slice := []int{1, 2, 3}
addNum(slice)
fmt.Println(slice)
}
// 결과
[1 2 3]
- slice := []int{1, 2, 3}을 addNum의 인자를 새로 넣었는데, 이는 main의 slice와 다른 인스턴스임
- 호출 될 때 main slice 값으로 Num slice에 type size만큼 복사해서 main과 동일함. (포인터, len, cap 동일)
- main slice와 Num slice는 메모리 주소가 다름!!!
- append(slice, 4)를 수행하기 위해 새로운 공간을 할당하여 기존 값을 복사하고 4를 추가하여 반환(Num slice값 변경:포인터, len, cap)
- main slice는 영향이 없음 → 기존 배열을 그대로 가리키고 있음 → 그렇기 때문에 결과가 바뀌지 않음
이를 해소하기 위해 아래와 같이 코드를 변경할 수 있음.
package main
import "fmt"
func addNum(slice *[]int) {
*slice = append(*slice, 4)
}
func main() {
slice := []int{1, 2, 3}
addNum(&slice)
fmt.Println(slice)
}
// 결과
[1 2 3 4]
- Num slice를 메모리 공간이 가르키는 값과 포인터 주소로 변경 = main slice를 가리킴(같은 인스턴스를 가리킴)
package main
import "fmt"
func addNum(slice []int) []int {
slice = append(slice, 4)
return slice
}
func main() {
slice := []int{1, 2, 3}
slice = addNum(slice)
fmt.Println(slice)
}
// 결과
[1 2 3 4]
- 새로운 슬라이스를 반환하는 방법으로 변경할 수도 있음(값을 넘겨서 새로운 값으로 반환)
슬라이싱(Slicing)
- 배열의 일부를 집어내는 기능
- 슬라이싱 기능을 사용하면 그 결과로 슬라이스를 반환
* startIdx: 시작 인덱스, endindex: 끝 인덱스
array[startIdx:endindex]
→ 배열의 일부를 집어낼 때는 대상이 되는 배열을 쓰고 대괄호[] 사이에 집어내고자 하는 시작인덱스:끝인덱스를 씀
→ 배열의 시작인덱스부터 끝인덱스-1까지의 배열 일부를 나타내는 슬라이스가 반환
※ 주의할 점, 끝 인덱스를 포함하지 않고 끝 인덱스 하나 전까지 포함.
✓ 슬라이싱은 새로운 배열이 만들어지는 게 아니라 배열의 일부를 포인터로 가르키는 슬라이스를 만들어냄!
package main
import "fmt"
func main() {
array := [5]int{1, 2, 3, 4, 5}
slice := array[1:2]
fmt.Println("array:", array)
fmt.Println("slice:", slice, len(slice), cap(slice))
}
// 결과
array: [1 2 3 4 5]
slice: [2] 1 4
- array의 두 번째부터 세 번째전까지 출력하고, len은 end - start 값, cap은 전체 배열에서 시작 주소를 뺀 값을 출력
package main
import "fmt"
func main() {
array := [5]int{1, 2, 3, 4, 5}
slice := array[1:2]
fmt.Println("array:", array)
fmt.Println("slice:", slice, len(slice), cap(slice))
array[1] = 100
fmt.Println("After change second element")
fmt.Println("array:", array)
fmt.Println("slice:", slice, len(slice), cap(slice))
}
// 결과
array: [1 2 3 4 5]
slice: [2] 1 4
After change second element
array: [1 100 3 4 5]
slice: [100] 1 4
- array의 첫 번째 자리를 100으로 바꾸면, 슬라이싱은 새로운 배열을 만드는 것이 아니고 가르키는 것이기 때문에 100을 출력
package main
import "fmt"
func main() {
array := [5]int{1, 2, 3, 4, 5}
slice := array[1:2]
fmt.Println("array:", array)
fmt.Println("slice:", slice, len(slice), cap(slice))
array[1] = 100
fmt.Println("After change second element")
fmt.Println("array:", array)
fmt.Println("slice:", slice, len(slice), cap(slice))
slice = append(slice, 500)
fmt.Println("After append 500")
fmt.Println("array:", array)
fmt.Println("slice:", slice, len(slice), cap(slice))
}
// 결과
array: [1 2 3 4 5]
slice: [2] 1 4
After change second element
array: [1 100 3 4 5]
slice: [100] 1 4
After append 500
array: [1 100 500 4 5]
slice: [100 500] 2 4
- array에 두 번째와 세 번째 값을 100, 500 slice에 100, 500을 출력
- 처음에 만들었을 때 len이 1이고, cap이 4라서 빈공간이 충분하다고 생각하여 500을 추가
- slice의 메모리 배열은 기존 배열이기 때문에 array의 값도 같이 바뀜
슬라이스를 슬라이싱하기
- 슬라이싱은 슬라이스 일부를 집어낼 때도 사용할 수 있음
slice1 := []int{1, 2, 3, 4, 5}
slice2 := slice1[1:2] // slice2는 [2]
- slice1은 [1, 2, 3, 4, 5]의 5개 요소를 갖는 배열을 가리키고,
slice2는 slice1이 가리키는 배열의 시작 인덱스 1인 두 번째 요소를 가르키는 슬라이스가 됨.
- slice2의 len은 1로 2값을 갖는 요소 하나만 갖고, cap은 4로 전체 길이에서 시작 인덱스를 뺀 값이 됨.
* Python의 슬라이싱과 Go의 슬라이싱은 다르다!
a = [1,2,3]
b = a[0:2]
b[0] = 100
print(a)
print(b)
// 결과
[1,2,3]
[100,2]
※ python에서는 슬라이싱을 하면 새로운 배열이 나오기 때문에 Go와 다름!!
<처음부터 슬라이싱>
slice1 := []int{1, 2, 3, 4, 5}
slice2 := slice1[0:3] // slice2는 [1, 2, 3]
- slice2는 slice1의 첫 번째부터 세 번째까지 집어냄.
- cap 값은 slice1이 가리키는 배열의 전체 길이 5에서 시작인덱스인 0을 뺀 결과인 5가 됨.
- 처음부터 슬라이싱할 때는 아래와 같이 0을 제외하고 사용할 수 있음.
// 두 개가 같은 구문
slice2 := slice[0:3]
slice2 := slice1[:3] // 처음부터 슬라이싱할 때 0을 뺄 수 있음
<끝까지 슬라이싱>
slice1 := []int{1, 2, 3, 4, 5}
slice2 := slice1[2:len(slice1)] // slice2는 [3, 4, 5]
- slice2는 slice1의 세 번째부터 끝까지 집어냄
- cap은 배열 전체 길이 5에서 시작 인덱스 2를 뺀 3을 갖음.
- 끝까지 슬라이싱 하는 경우 끝 인덱스를 생략할 수 있음.
★ python에서는 [2:-1] 끝에서 하나 전까지의 의미로 음수 사용이 가능하지만, Go는 지원 안함!
Go는 [2: len(slice)-1]의 형태로 사용해야 함!
// 두 개가 같은 구문
slice2 := slice[2:len(slice1)]
slice2 := slice1[2:] // 끝까지 슬라이싱할 때
<전체 슬라이싱>
// 두 개가 같은 구문
array := [5]int{1, 2, 3, 4, 5}
slice := array[:] // 전체 슬라이스
- array 배열을 슬라이싱 하는데 시작과 끝 인덱스를 생략했을 때, 처음부터 끝까지 슬라이싱하는 것을 의미
- 전체 슬라이싱은 배열 전체를 가리키는 슬라이스를 만들고 싶을 때 주로 사용
<인덱스 3개로 슬라이싱해 cap 크기 조절>
- 인덱스를 2개만 사용할 때 cap은 배열의 전체 길이에서 시작 인덱스를 뺀 값이 됨.
- 슬라이싱 할 때 인덱스 3개를 사용해서 cap까지 조절할 수 있음
slice[ 시작 인덱스 : 끝 인덱스 : 최대 인덱스 ]
- 시작 인덱스부터 끝 인덱스 하나 전까지 집어내고 최대 인덱스까지만 배열을 사용함
- 집어낸 슬라이스의 cap값은 최대 인덱스 - 시작 인덱스가 됨.
slice1 := []int{1, 2, 3, 4, 5}
slice2 := slice1[1:3:4]
→ slice[1:3:4]를 한 경우 slice1이 가리키는 전체 배열 길이(즉 5개)를 다 쓰는게 아니라 인덱스 4까지만 배열을 사용
즉, 배열의 두 번째부터 네 번째까지만 배열을 사용하게 됨. (3개의 공간에 2개를 사용하고 있는 슬라이스가 됨)
슬라이스 복제
- slice1이 가리키는 배열과 똑같은 배열을 복제한 뒤 slice2가 가리키게하면 서로 다른 배열을 가리킴
- slice1의 요솟값을 변경해도 slice2값은 변경되지 않음.
package main
import "fmt"
func main() {
slice1 := []int{1, 2, 3, 4, 5}
slice2 := make([]int, len(slice1))
for i, v := range slice1 {
slice2[i] = v
}
slice2[1] = 100
fmt.Println("slice1", slice1)
fmt.Println("slice2", slice2)
}
// 결과
slice1 [1 2 3 4 5]
slice2 [1 100 3 4 5]
- slice1과 똑같은 길이의 다른 슬라이스를 만들어 요솟값을 하나씩 돌면서 복사함.
package main
import "fmt"
func main() {
slice1 := []int{1, 2, 3, 4, 5}
slice2 := append([]int{}, slice1...)
slice2[1] = 100
fmt.Println("slice1", slice1)
fmt.Println("slice2", slice2)
}
// 결과
slice1 [1 2 3 4 5]
slice2 [1 100 3 4 5]
- append() 함수를 사용하여 slice1의 모든 값을 복제한 새로운 슬라이스를 만듦.
- 배열이나 슬라이스 뒤에 ...을 사용하면 모든 요솟값을 넣어준 것과 같음
slice2 := append([]int{} slice1[0], slice1[1], slice1[2], slice1[3], slice1[4])
package main
import "fmt"
func main() {
slice1 := []int{1, 2, 3, 4, 5}
slice2 := make([]int, len(slice1))
copy(slice2, slice1)
slice2[1] = 100
fmt.Println("slice1", slice1)
fmt.Println("slice2", slice2)
}
// 결과
slice1 [1 2 3 4 5]
slice2 [1 100 3 4 5]
- 내장함수 copy()를 사용해 복사할 수 있음
- 첫 번째 인수로 복사한 결과를 저장하는 슬라이스 변수를 넣고, 두 번째 인수로 복사 대상이 되는 슬라이스 변수를 넣음
- 반환값은 실제로 복사된 요소 개수
- 실제 복사되는 요소 개수는 목적지의 슬라이스 길이와 대상의 슬라이스 길이 중 작은 개수만큼 복사
func copy(dst, src []Type) int
요소 삭제
- 슬라이스 중간의 요소를 삭제할 수 있음
- 중간 요소를 삭제하고, 중간 요소 이후의 값을 앞당겨서 삭제된 요소를 채우고 마지막 값을 지워야함
package main
import "fmt"
func main() {
slice := []int{1, 2, 3, 4, 5, 6}
idx := 2
for i := idx + 1; i < len(slice); i++ {
slice[i-1] = slice[i]
}
slice = slice[:len(slice)-1]
fmt.Println(slice)
}
// 결과
[1 2 4 5 6]
- 삭제할 인덱스(idx)를 2로 그 다음부터 끝까지 뒤에부터 앞으로 하나씩 복사
- 맨 마지막은 -1로 잘라내면 요소를 앞당길 수 있음.
package main
import "fmt"
func main() {
slice := []int{1, 2, 3, 4, 5, 6}
idx := 2
slice = append(slice[:idx], slice[idx+1:]...)
//for i := idx + 1; i < len(slice); i++ {
// slice[i-1] = slice[i]
//}
//slice = slice[:len(slice)-1]
fmt.Println(slice)
}
// 결과
[1 2 4 5 6]
- append를 사용하여 한 줄로 줄여서 사용할 수 있음.
slice = append(slice[:idx], slice[idx+1:]...)
- slice[:idx]는 slice 처음부터 idx 하나 전까지 집어낸 슬라이스. 즉, 지우고자 하는 인덱스의 요소는 포함하지 않음
= [1, 2]
- slice[idx+1:]는 idx 하나 뒤의 값부터 끝까지 슬라이스.
= [1, 2, 4, 5, 6]
- append()함수로 [1, 2] 슬라이스 뒤에 [4, 5, 6]을 붙이면 [1, 2, 4, 5, 6]이 됨.
package main
import "fmt"
func main() {
slice := []int{1, 2, 3, 4, 5, 6}
idx := 2
copy(slice[idx:], slice[idx+1:])
// slice = slice[:len(slice)-1]
fmt.Println(slice)
}
// 결과
[1 2 4 5 6]
- copy(dst, src) 구조로 copy 함수를 통해 요소를 삭제할 수 있음
요소 삽입
- 슬라이스 중간에 요소를 추가할 수 있음
- 슬라이스 맨 뒤에 요소를 하나 추가하고, 맨 뒤값부터 삽입하려는 위치까지 한 칸씩 뒤로 밀어주고 삽입하는 위치의 값을 바꿈
package main
import "fmt"
func main() {
slice := []int{1, 2, 3, 4, 5, 6}
slice = append(slice, 0)
idx := 2
for i := len(slice) - 2; i >= idx; i-- {
slice[i+1] = slice[i]
}
slice[idx] = 100
fmt.Println(slice)
}
// 결과
[1 2 100 3 4 5 6]
- 맨 뒤에 요소를 추가하는데 추가하는 값은 중요하지 않아 0으로 처리
- 맨 뒤부터 삽입하는 자리까지 하나씩 뒤로 밀고, idx 위치의 값을 바꿈
package main
import "fmt"
func main() {
slice := []int{1, 2, 3, 4, 5, 6}
idx := 2
slice = append(slice[:idx], append([]int{100}, slice[idx:]...)...)
fmt.Println(slice)
}
// 결과
[1 2 100 3 4 5 6]
- append()를 중첩으로 사용할 수 있음
- slice[:idx]는 처음부터 삽입하는 위치 전까지
= [1, 2]
- []int{100}은 삽입하려는 값으로 100 한 개만 갖는 슬라이스
- slice[idx:]는 삽입하려는 위치부터 끝까지의 슬라이스
= [3, 4, 5, 6]
- append() 함수로 []int{100}에 이 슬라이스를 합쳐 [100, 3, 4, 5, 6]을 만듦
- append() 함수로 [1, 2]에 [100, 3, 4, 5, 6]을 합쳐서 [1, 2, 100, 3, 4, 5, 6]을 만듦
package main
import "fmt"
func main() {
slice := []int{1, 2, 3, 4, 5, 6}
idx := 2
slice = append(slice, 0)
copy(slice[idx+1:], slice[idx:])
slice[idx] = 100
fmt.Println(slice)
}
// 결과
[1 2 100 3 4 5 6]
- copy()를 통해 슬라이스 값을 복사(for문과 비슷한 형식)
- 해당 방법이 불필요한 메모리 사용이 없음!
슬라이스 정렬
- Go 언어에서 기본 제공하는 sort 패키지를 사용해 슬라이스 정렬을 할 수 있음
package main
import (
"fmt"
"sort"
)
func main() {
slice := []int{5, 2, 6, 3, 1, 4}
sort.Ints(slice)
fmt.Println(slice)
}
// 결과
[1 2 3 4 5 6]
구조체 슬라이스 정렬
- sort() 함수를 이용하기 위해 len(), less(), swap() 세 메서드가 필요함.
- 해당 메서드만 구현하면, 정의한 구조체도 정렬을 할 수가 있음.
package main
import (
"fmt"
"sort"
)
type Student struct {
Name string
Age int
}
type Students []Student
func (s Students) Len() int { return len(s) }
func (s Students) Less(i, j int) bool { return s[i].Age < s[j].Age }
func (s Students) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func main() {
s := []Student{
{"화랑", 32}, {"백두산", 53}, {"류", 43}, {"켄", 38}, {"송하나", 18}}
sort.Sort(Students(s))
fmt.Println(s)
}
// 결과
[{송하나 18} {화랑 32} {켄 38} {류 43} {백두산 53}]
'Language > Golang' 카테고리의 다른 글
Golang (Go언어) 슬라이스(Slice) 1/2 (0) | 2024.11.19 |
---|---|
Golang (Go언어) 패키지(Package) (6) | 2024.11.09 |
Golang (Go언어) 문자열(String) (4) | 2024.11.08 |
Golang (Go언어) 포인터(Pointer) (3) | 2024.11.07 |
Golang (Go언어) 구조체(Structure) (9) | 2024.11.05 |