diff --git a/16-slices/06-slice-expressions/2-example/main.go b/16-slices/06-slice-expressions/2-example/main.go index b30172b..f1b8f56 100644 --- a/16-slices/06-slice-expressions/2-example/main.go +++ b/16-slices/06-slice-expressions/2-example/main.go @@ -17,18 +17,9 @@ func main() { // think of this as search results of a search engine. // it could have been fetched from a database items := []string{ - "pacman", - "mario", - "tetris", - "doom", - "galaga", - "frogger", - "asteroids", - "simcity", - "metroid", - "defender", - "rayman", - "tempest", + "pacman", "mario", "tetris", "doom", + "galaga", "frogger", "asteroids", "simcity", + "metroid", "defender", "rayman", "tempest", "ultima", } diff --git a/x-tba/slices/09-slice-internals-1-backing-array/1-theory/main.go b/16-slices/08-slice-internals-1-backing-array/1-theory/main.go similarity index 100% rename from x-tba/slices/09-slice-internals-1-backing-array/1-theory/main.go rename to 16-slices/08-slice-internals-1-backing-array/1-theory/main.go diff --git a/x-tba/slices/09-slice-internals-1-backing-array/2-example/main.go b/16-slices/08-slice-internals-1-backing-array/2-example/main.go similarity index 100% rename from x-tba/slices/09-slice-internals-1-backing-array/2-example/main.go rename to 16-slices/08-slice-internals-1-backing-array/2-example/main.go diff --git a/16-slices/09-slice-internals-2-slice-header/1-theory/main.go b/16-slices/09-slice-internals-2-slice-header/1-theory/main.go new file mode 100644 index 0000000..49dabef --- /dev/null +++ b/16-slices/09-slice-internals-2-slice-header/1-theory/main.go @@ -0,0 +1,46 @@ +// For more tutorials: https://blog.learngoprogramming.com +// +// Copyright © 2018 Inanc Gumus +// Learn Go Programming Course +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ +// + +package main + +import ( + "fmt" + + s "github.com/inancgumus/prettyslice" +) + +func main() { + // + // each int element is 4 bytes (on 64-bit) + // + // let's say the ages point to 1000th. + // ages[1:] will point to 1004th + // ages[2:] will point to 1008th and so on. + // + // they all will be looking at the same + // backing array. + // + + ages := []int{35, 15, 25} + red, green := ages[0:1], ages[1:3] + + s.Show("ages", ages) + s.Show("red", red) + s.Show("green", green) + + fmt.Println(red[0]) + // fmt.Println(red[1]) // error + // fmt.Println(red[2]) // error + + { + var ages []int + s.Show("nil slice", ages) + + // or just: + s.Show("nil slice", []int(nil)) + } +} diff --git a/x-tba/slices/10-slice-internals-2-slice-header/2-example/main.go b/16-slices/09-slice-internals-2-slice-header/2-example/main.go similarity index 96% rename from x-tba/slices/10-slice-internals-2-slice-header/2-example/main.go rename to 16-slices/09-slice-internals-2-slice-header/2-example/main.go index 65f5c70..d5e1f5e 100644 --- a/x-tba/slices/10-slice-internals-2-slice-header/2-example/main.go +++ b/16-slices/09-slice-internals-2-slice-header/2-example/main.go @@ -19,7 +19,7 @@ type collection []string // #2 // go is pass by copy // only the slice header is copied: 3 integer fields (24 bytes) -// think of passing an array with millions of elements. +// think of passing an array with millions of elements instead. func main() { // SliceHeader lives here: diff --git a/16-slices/exercises/16-internals-backing-array-fix/main.go b/16-slices/exercises/16-internals-backing-array-fix/main.go new file mode 100644 index 0000000..c15c56f --- /dev/null +++ b/16-slices/exercises/16-internals-backing-array-fix/main.go @@ -0,0 +1,60 @@ +// For more tutorials: https://blog.learngoprogramming.com +// +// Copyright © 2018 Inanc Gumus +// Learn Go Programming Course +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ +// + +package main + +import ( + "fmt" + "math/rand" + "time" +) + +// --------------------------------------------------------- +// EXERCISE: Fix the backing array problem +// +// You receive numbers from an API. After you're done working +// with it, the API needs to continue using those numbers. +// +// But your program changes the numbers (changes the API's slice). +// +// Fix the program so that your program doesn't modify +// the original numbers. +// +// +// RESTRICTION +// +// Fix your problem only in the designated area of the code below. +// +// +// EXPECTED OUTPUT +// +// Mine : [-50 -100 -150] +// Original nums: [56 89 15] +// +// Note: Original nums may vary (they're random) +// But your slice should look like the above (mine slice) +// +// Yes, it should output only three numbers for the both slices! +// +// --------------------------------------------------------- + +func main() { + // API returns random numbers in an int slice + rand.Seed(time.Now().UnixNano()) + nums := rand.Perm(100) + + // ---------------------------------------- + // RESTRICTIONS — ONLY ADD YOUR CODE HERE + // + mine := nums + // + // ---------------------------------------- + + mine[0], mine[1], mine[2] = -50, -100, -150 + fmt.Println("Mine :", mine) + fmt.Println("Original nums:", nums[:3]) +} diff --git a/16-slices/exercises/16-internals-backing-array-fix/solution/main.go b/16-slices/exercises/16-internals-backing-array-fix/solution/main.go new file mode 100644 index 0000000..f9419fc --- /dev/null +++ b/16-slices/exercises/16-internals-backing-array-fix/solution/main.go @@ -0,0 +1,35 @@ +// For more tutorials: https://blog.learngoprogramming.com +// +// Copyright © 2018 Inanc Gumus +// Learn Go Programming Course +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ +// + +package main + +import ( + "fmt" + "math/rand" + "time" +) + +func main() { + rand.Seed(time.Now().UnixNano()) + nums := rand.Perm(100) + + // ---------------------------------------- + // breaks the connection: + // mine and nums now have different backing arrays + + // verbose solution: + // var mine []int + // mine = append(mine, nums[:3]...) + + // better solution (almost the same thing): + mine := append([]int(nil), nums[:3]...) + // ---------------------------------------- + + mine[0], mine[1], mine[2] = -50, -100, -150 + fmt.Println("Mine :", mine) + fmt.Println("Original nums:", nums[:3]) +} diff --git a/16-slices/exercises/17-internals-backing-array-sort/main.go b/16-slices/exercises/17-internals-backing-array-sort/main.go new file mode 100644 index 0000000..b993d2e --- /dev/null +++ b/16-slices/exercises/17-internals-backing-array-sort/main.go @@ -0,0 +1,52 @@ +// For more tutorials: https://blog.learngoprogramming.com +// +// Copyright © 2018 Inanc Gumus +// Learn Go Programming Course +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ +// + +package main + +import ( + "fmt" +) + +// --------------------------------------------------------- +// EXERCISE: Sort the backing array +// +// 1. Sort only the middle 3 items. +// +// 2. All the slices should see your change. +// +// +// RESTRICTION +// +// Do not sort manually. Sort by slicing then by using the sort package. +// +// +// EXPECTED OUTPUT +// +// Original: [pacman mario tetris doom galaga frogger asteroids simcity metroid defender rayman tempest ultima] +// +// Sorted : [pacman mario tetris doom galaga asteroids frogger simcity metroid defender rayman tempest ultima] +// +// +// HINT: +// +// Middle items are : [frogger asteroids simcity] +// +// After sorting they become: [asteroids frogger simcity] +// +// --------------------------------------------------------- + +func main() { + items := []string{ + "pacman", "mario", "tetris", "doom", "galaga", "frogger", + "asteroids", "simcity", "metroid", "defender", "rayman", + "tempest", "ultima", + } + + fmt.Println("Original:", items) + fmt.Println() + fmt.Println("Sorted :", items) +} diff --git a/16-slices/exercises/17-internals-backing-array-sort/solution/main.go b/16-slices/exercises/17-internals-backing-array-sort/solution/main.go new file mode 100644 index 0000000..f58a30f --- /dev/null +++ b/16-slices/exercises/17-internals-backing-array-sort/solution/main.go @@ -0,0 +1,33 @@ +// For more tutorials: https://blog.learngoprogramming.com +// +// Copyright © 2018 Inanc Gumus +// Learn Go Programming Course +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ +// + +package main + +import ( + "fmt" + "sort" +) + +func main() { + items := []string{ + "pacman", "mario", "tetris", "doom", "galaga", "frogger", + "asteroids", "simcity", "metroid", "defender", "rayman", + "tempest", "ultima", + } + + fmt.Println("Original:", items) + + mid := len(items) / 2 + smid := items[mid-1 : mid+2] + + // sorting the smid will affect the items + // as well. their backing array is the same. + sort.Strings(smid) + + fmt.Println() + fmt.Println("Sorted :", items) +} diff --git a/16-slices/exercises/18-internals-slice-header/main.go b/16-slices/exercises/18-internals-slice-header/main.go new file mode 100644 index 0000000..779d6cc --- /dev/null +++ b/16-slices/exercises/18-internals-slice-header/main.go @@ -0,0 +1,115 @@ +package main + +import ( + "fmt" + "runtime" + "runtime/debug" +) + +// --------------------------------------------------------- +// EXERCISE: Observe the memory allocations +// +// In this exercise, your goal is to observe the memory allocation +// differences between arrays and slices. +// +// You will create, assign arrays and slices then you will print +// the memory usage of your program on each step. +// +// Please follow the instructions inside the code. +// +// +// EXPECTED OUTPUT +// +// Note that, your memory usage numbers may vary. These are on my +// own system. However, the size of the arrays and slices should be +// the same on your own system as well (if you're on 64-bit machine). +// +// +// <<< initial memory usage >>> +// > Memory Usage: 104 KB +// <<< after declaring an array >>> +// > Memory Usage: 78235 KB +// <<< after copying the array >>> +// > Memory Usage: 156365 KB +// <<< inside passArray >>> +// > Memory Usage: 234495 KB +// <<< after slicings >>> +// > Memory Usage: 234497 KB +// <<< inside passSlice >>> +// > Memory Usage: 234497 KB +// +// Array's size : 80000000 bytes. +// Array2's size: 80000000 bytes. +// Slice1's size: 24 bytes. +// Slice2's size: 24 bytes. +// Slice3's size: 24 bytes. +// +// +// HINTS +// +// I've declared a few function to help you. +// +// report function prints the memory usage. +// Just call it with a message that matches to the expected output. +// +// passArray function accepts a [size]int array, so you can pass it +// your array. It automatically prints the memory usage. +// +// passSlice function accepts an int slice, so you can pass it +// your one of your slices. It automatically prints the memory usage. +// +// --------------------------------------------------------- + +const size = 1e7 + +func main() { + // stops the gc: prevents cleaning up the memory + debug.SetGCPercent(-1) + + // run the program to see what this prints + report("initial memory usage") + + // 1. allocate an array with 10 million int elements + // this array's size is equal to ~80MB + // hint: use the `size` constant + // + // 2. print the memory usage + + // 3. copy the array to a new array (just assign) + // 4. print the memory usage + + // 5. pass the array to passArray function + + // 6. convert the array to a slice (by slicing) + // 7. slice only the first 1000 elements of the array + // 8. slice only the elements of the array between 1000 and 10000 + // 9. print the memory usage + + // 10. pass the one of the slices to passSlice function + + // 11. print the sizes of the arrays and slices + // hint: use the unsafe.Sizeof function +} + +// observe that passing an array affects the memory usage dramatically +// +// passes [size]int array — about 80MB! +func passArray(items [size]int) { + items[0] = 100 + report("inside passArray") +} + +// observe that passing a slice doesn't affect the memory usage +// +// only passes 24-bytes of slice header +func passSlice(items []int) { + items[0] = 100 + report("inside passSlice") +} + +func report(msg string) { + var m runtime.MemStats + runtime.ReadMemStats(&m) + fmt.Printf("<<< %s >>>\n", msg) + fmt.Printf("\t> Memory Usage: %v KB\n", m.Alloc/1024) +} diff --git a/16-slices/exercises/18-internals-slice-header/solution/main.go b/16-slices/exercises/18-internals-slice-header/solution/main.go new file mode 100644 index 0000000..6dab275 --- /dev/null +++ b/16-slices/exercises/18-internals-slice-header/solution/main.go @@ -0,0 +1,62 @@ +// For more tutorials: https://blog.learngoprogramming.com +// +// Copyright © 2018 Inanc Gumus +// Learn Go Programming Course +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ +// + +package main + +import ( + "fmt" + "runtime" + "runtime/debug" + "unsafe" +) + +const size = 1e7 + +func main() { + // stops the gc: prevents cleaning up the memory + debug.SetGCPercent(-1) + + report("initial memory usage") + + var array [size]int + report("after declaring an array") + + array2 := array + report("after copying the array") + + passArray(array) + + slice1 := array[:] + slice2 := array[1e3:] + slice3 := array[1e3:1e4] + report("after slicings") + + passSlice(slice3) + + fmt.Println() + fmt.Printf("Array's size : %d bytes.\n", unsafe.Sizeof(array)) + fmt.Printf("Array2's size: %d bytes.\n", unsafe.Sizeof(array2)) + fmt.Printf("Slice1's size: %d bytes.\n", unsafe.Sizeof(slice1)) + fmt.Printf("Slice2's size: %d bytes.\n", unsafe.Sizeof(slice2)) + fmt.Printf("Slice3's size: %d bytes.\n", unsafe.Sizeof(slice3)) +} + +func passArray(items [size]int) { + items[0] = 100 + report("inside passArray") +} + +func passSlice(items []int) { + report("inside passSlice") +} + +func report(msg string) { + var m runtime.MemStats + runtime.ReadMemStats(&m) + fmt.Printf("<<< %s >>>\n", msg) + fmt.Printf("\t> Memory Usage: %v KB\n", m.Alloc/1024) +} diff --git a/16-slices/exercises/README.md b/16-slices/exercises/README.md index 22f658f..60c573e 100644 --- a/16-slices/exercises/README.md +++ b/16-slices/exercises/README.md @@ -41,4 +41,14 @@ These are warm-up exercises that will reinforce your knowledge of slices. 2. **[Slicing by arguments](https://github.com/inancgumus/learngo/tree/master/16-slices/exercises/14-slicing-by-args)** -3. **[Slicing the Housing Prices](https://github.com/inancgumus/learngo/tree/master/16-slices/exercises/15-slicing-housing-prices)** \ No newline at end of file +3. **[Slicing the Housing Prices](https://github.com/inancgumus/learngo/tree/master/16-slices/exercises/15-slicing-housing-prices)** + +--- + +## Exercises Level IV - Internals + +1. **[Fix the backing array problems](https://github.com/inancgumus/learngo/tree/master/16-slices/exercises/16-internals-backing-array-fix)** + +2. **[Sort the backing array](https://github.com/inancgumus/learngo/tree/master/16-slices/exercises/17-internals-backing-array-sort)** + +3. **[Observe the memory allocations](https://github.com/inancgumus/learngo/tree/master/16-slices/exercises/18-internals-slice-header)** diff --git a/16-slices/questions/4-backing-array.md b/16-slices/questions/4-backing-array.md new file mode 100644 index 0000000..d2b5ef2 --- /dev/null +++ b/16-slices/questions/4-backing-array.md @@ -0,0 +1,131 @@ +# Backing Array Quiz + +## Where does a slice store its elements? +1. In the slice value +2. In a global backing array that is shared by all the slices +3. In a backing array that is specific to a slice +4. In a backing array that the slice references *CORRECT* + +> **1:** A slice value doesn't store any elements. It's just a simple data structure. +> +> **2:** There is not a global backing array. +> +> **3:** A backing array can be shared among slices. It may not be specific to a slice. +> +> **4:** Yep! A slice stores its elements in a backing that the slice references (or points to). +> + + +## When you slice a slice, what value does it return? +```go +// example: +s := []string{"i'm", "a", "slice"} +s[2:] // <-- slicing +``` +1. It returns a new slice value with a new backing array +2. It returns the existing slice value with a new backing array +3. It returns a new slice value with the same backing array *CORRECT* + +> **3:** Yes! Slicing returns a new slice that references to some segment of the same backing array. + + +## Why is slicing and indexing a slice efficient? +1. Slices are fast +2. Backing arrays are contiguous in memory *CORRECT* +3. Go uses clever algorithms + +> **2:** Yes. A slice's backing array is contiguous in memory. So, accessing an element of a slice is very fast. Go can look at a specific memory location to find an element's value very fast. + + +## Which one is the backing array of "slice2"? +```go +arr := [...]int{1, 2, 3} +slice1 := arr[2:3] +slice2 := slice1[:1] +``` + +1. arr *CORRECT* +2. slice1 +3. slice2 +4. A hidden backing array + +> **1:** Yes! When a slice is created by slicing an array, that array becomes the backing array of that slice. +> +> **4:** Nope. That only happens when a slice doesn't being created from an array. +> + + +## Which one is the backing array of "slice"? +```go +arr := [...]int{1, 2, 3} +slice := []int{1, 2, 3} +``` + +1. arr +2. slice1 +3. slice2 +4. A hidden backing array *CORRECT* + +> **1:** Nope, the slice hasn't created by slicing an array. +> +> **4:** Yes! A slice literal always creates a new hidden array. +> + + +## Which answer is correct for the following slices? +```go +slice1 := []int{1, 2, 3} +slice2 := []int{1, 2, 3} +``` +1. Their backing array is the same. +2. Their backing arrays are different. *CORRECT* +3. They don't have any backing arrays. + +> **2:** That's right. A slice literal always creates a new backing array. + + +## Which answer is correct for the following slices? +```go +slice1 := []int{1, 2, 3} +slice2 := []int{1, 2, 3} +slice3 := slice1[:] +slice4 := slice2[:] +``` +1. slice1 and slice2 have the same backing arrays. +2. slice1 and slice3 have the same backing arrays. *CORRECT* +3. slice1 and slice4 have the same backing arrays. +4. slice3 and slice4 have the same backing arrays. + +> **2:** Yep! A slice that is being created by slicing shares the same backing with the sliced slice. Here, slice3 is being created from slice1. That is also true for slice2 and slice4. + + +## What does the backing array of the nums slice look like? +```go +nums := []int{9, 7, 5, 3, 1} +nums = nums[:1] + +fmt.Println(nums) // prints: [9] +``` +1. [9 7 5 3 1] *CORRECT* +2. [7 5 3 1] +3. [9] +4. [] + + +## What does this code print? +```go +arr := [...]int{9, 7, 5, 3, 1} +nums := arr[2:] +nums2 := nums[1:] + +arr[2]++ +nums[1] -= arr[4] - 4 +nums2[1] += 5 + +fmt.Println(nums) +``` +1. [5 3 1] +2. [6 6 6] *CORRECT* +3. [9 7 5] + +> **2:** Yes! Because the backing array of `nums` and `nums2` is the same: `arr`. See the explanation here: https://play.golang.org/p/xTy0W0S_8PN diff --git a/16-slices/questions/5-slice-header.md b/16-slices/questions/5-slice-header.md new file mode 100644 index 0000000..16b19e9 --- /dev/null +++ b/16-slices/questions/5-slice-header.md @@ -0,0 +1,90 @@ +# Slice Header Quiz + +## What is a slice header? +1. The first element of a slice value +2. The first element of the backing array +3. A tiny data structure that describes all or some part of a backing array *CORRECT* +4. A data structure that contains the elements of a slice + +> **3:** Yes! It's just a tiny data structure with three numeric fields. +> +> **4:** A slice doesn't contain any elements on its own. + + +## What are the fields of a slice value? +1. Pointer, length, and capacity *CORRECT* +2. Length and capacity +3. Only a pointer + + +## Which slice value does the following slice header describe? +SLICE HEADER: ++ Pointer : 100th ++ Length : 5 ++ Capacity: 10 + +Assume that the backing array is this one: +```go +var array [10]string +``` +1. array[5:] +2. array[:5] *CORRECT* +3. array[3:] +4. array[100:] + +> **1**: This slice's capacity is 5, it can only see the elements beginning with the 6th element. +> +> **2**: That's right. `array[:5]` returns a slice with the first 5 elements of the `array` (len is 5), but there are 5 more elements in the backing array of that slice, so in total its capacity is 10. +> +> **3**: This slice's capacity is 7, it can only see the elements beginning with the 4th element. +> +> **4**: This is an error. The backing array doesn't have 100 elements. +> + + +## Which one is the slice header of the following slice? +```go +var tasks []string +``` +1. Pointer: 0, Length: 0, Capacity: 0 *CORRECT* +2. Pointer: 10, Length: 5, Capacity: 10 +3. Pointer: 0, Length: 1, Capacity: 1 + +> **1:** A nil slice doesn't have backing array, so all the fields are equal to zero. + + +## What is the total memory usage of this code? +```go +var array [1000]int64 + +array2 := array +slice := array2[:] +``` + +1. 1024 bytes +2. 2024 bytes +3. 3000 bytes +4. 16024 bytes *CORRECT* + +> **4:** `array` is 1000 x int64 (8 bytes) = 8000 bytes. Assigning an array copies all its elements, so `array2` adds additional 8000 bytes. A slice doesn't store anything on its own. Here, it's being created from array2, so it doesn't allocate a backing array as well. A slice header's size is 24 bytes. So in total: This program allocates 16024 bytes. + + +## What value does this code pass to the sort.Ints function? +```go +nums := []int{9, 7, 5, 3, 1} +sort.Ints(nums) +``` +1. [9 7 5 3 1] — All the values of the nums slice +2. A pointer to the backing array of the nums slice +3. A pointer, length and capacity as three different arguments +4. The slice header that is stored in the nums variable *CORRECT* + +> **1:** No, a slice value doesn't contain any elements. So it cannot pass the elements. +> +> **2:** Sorry but not only that. +> +> **3:** Nope. Remember, they are packed in a tiny data structure called the ....? +> +> **4:** Yep! A slice value is a slice header (pointer, length and capacity). A slice variable stores the slice header. +> + diff --git a/16-slices/questions/README.md b/16-slices/questions/README.md index 3095f6e..11d067f 100644 --- a/16-slices/questions/README.md +++ b/16-slices/questions/README.md @@ -4,4 +4,6 @@ * [Appending](2-appending.md) -* [Slicing](3-slicing.md) \ No newline at end of file +* [Slicing](3-slicing.md) + +* [Backing Array](4-backing-array.md) \ No newline at end of file diff --git a/x-tba/slices/10-slice-internals-2-slice-header/1-theory/main.go b/x-tba/slices/10-slice-internals-2-slice-header/1-theory/main.go deleted file mode 100644 index 2e8303d..0000000 --- a/x-tba/slices/10-slice-internals-2-slice-header/1-theory/main.go +++ /dev/null @@ -1,21 +0,0 @@ -// For more tutorials: https://blog.learngoprogramming.com -// -// Copyright © 2018 Inanc Gumus -// Learn Go Programming Course -// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ -// - -package main - -import s "github.com/inancgumus/prettyslice" - -func main() { - ages := []int{35, 15, 25} - first, last := ages[0:1], ages[1:3] - - s.Show("ages", ages) - s.Show("first", first) - s.Show("last", last) - - s.Show("nil slice", []int(nil)) -}