add: slice adv. ops. exercises

This commit is contained in:
Inanc Gumus
2019-03-05 23:32:32 +03:00
parent e379976af4
commit 9c96082326
19 changed files with 810 additions and 99 deletions

View File

@ -0,0 +1,143 @@
package main
import (
s "github.com/inancgumus/prettyslice"
)
// ---------------------------------------------------------
// EXERCISE: Practice advanced slice operations
//
// This exercise's intention is warm you up and reinforce
// your memory for the advanced slice operations.
//
// Please follow the directions in the following code.
//
// To see the expected output, please run:
//
// go run solution/main.go
//
// ---------------------------------------------------------
func main() {
//
// Use the prettyslice package for printing the slices,
// or do it with your own Printf that matches the output
// of the prettyslice package.
//
// This allows you to see the backing array of the slices.
s.PrintBacking = true
// Shows 10 slice elements per line
s.MaxPerLine = 10
// Prints 60 character per line
s.Width = 60
// ########################################################
//
// #1: Create a string slice with a length and capacity
// of 5. Call the slice: `names` and print it.
//
// ...
// s.Show("1st step", names)
// ########################################################
//
// #2: Append the following names to the names slice:
//
// "einstein", "tesla", "aristo"
//
// Print the names slice.
//
// Observe how the slice and its backing array change.
//
// ...
// s.Show("2nd step", names)
// ########################################################
//
// #3: Fix the append problem by reinitializing the
// names slice below (currently the previous code
// appends after 5 elements).
//
// Append the new elements to the head of the names
// slice instead.
//
// Print the names slice.
//
// HINT:
//
// The problem is in the `make` function.
//
// ...
// s.Show("3rd step", names)
// ########################################################
//
// #4: Copy elements from an array to the `names` slice.
//
// Currently, `cap(names)` is 5. So, copy only
// the first two elements of the following array
// to the last two elements of the `names` slice.
//
// Print the names slice (do not forget extending it).
// You should print 5 elements.
//
// Observe how the backing array stays the same.
//
// Array (uncomment):
// moreNames := [...]string{"plato", "khayyam", "ptolemy"}
// ...
// s.Show("4th step", names)
// ########################################################
//
// #5: Clone the `names` slice to the `clone` slice.
//
// But only clone (copy) the last 3 elements of the
// `names` slice.
//
// Then, append the first two elements of the `names`
// slice to the `clone` slice.
//
// Ensure that after appending no new backing array
// allocations occur for the `clone` slice.
//
// Print the clone slice before and after the append.
//
// ...
// s.Show("5th step (before append)", clone)
// ...
// s.Show("5th step (after append)", clone)
// ########################################################
//
// #6: Slice the `clone` slice between 2nd and 4th
// elements.
//
// Put the sliced slice into a variable named:
// `sliced`.
//
// Append "hypatia" to the `sliced` slice.
//
// Ensure that new backing array allocation "happens".
//
// Change the 3rd element of the `clone` slice
// to "elder".
//
// Doing so should not change any elements of
// the `sliced` slice.
//
// Print the `clone` and `sliced` slices.
//
// ...
// s.Show("6th step", clone, sliced)
}

View File

@ -0,0 +1,71 @@
// 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() {
s.PrintBacking = true
s.MaxPerLine = 10
s.Width = 60
//
// #1
//
names := make([]string, 5)
s.Show("1st step", names)
//
// #2
//
names = append(names, "einstein", "tesla", "aristo")
s.Show("2nd step", names)
//
// #3
//
names = make([]string, 0, 5)
names = append(names, "einstein", "tesla", "aristo")
s.Show("3rd step", names)
//
// #4
//
moreNames := [...]string{"plato", "khayyam", "ptolemy"}
copy(names[3:5], moreNames[:2])
names = names[:cap(names)]
s.Show("4th step", names)
//
// #5
//
clone := make([]string, 3, 5)
copy(clone, names[len(names)-3:])
s.Show("5th step (before append)", clone)
clone = append(clone, names[:2]...)
s.Show("5th step (after append)", clone)
//
// #6
//
sliced := clone[1:4:4]
sliced = append(sliced, "hypatia")
clone[2] = "elder"
s.Show("6th step", clone, sliced)
}

View File

@ -10,7 +10,7 @@ package main
import (
"fmt"
"github.com/inancgumus/learngo/16-slices/exercises/19-limit-the-backing-array-sharing/api"
"github.com/inancgumus/learngo/16-slices/exercises/23-limit-the-backing-array-sharing/api"
)
// ---------------------------------------------------------

View File

@ -10,12 +10,11 @@ package main
import (
"fmt"
"github.com/inancgumus/learngo/16-slices/exercises/19-limit-the-backing-array-sharing/solution/api"
"github.com/inancgumus/learngo/16-slices/exercises/23-limit-the-backing-array-sharing/solution/api"
)
func main() {
temps := api.Read(0, 3)
temps = append(temps, []int{1, 3}...)
fmt.Println("API's readings:", api.All())

View File

@ -0,0 +1,24 @@
package api
import (
"fmt"
"math/rand"
"runtime"
)
// DO NOT TOUCH THE FOLLOWING CODE
// THIS IS THE API
// YOU CANNOT CONTROL IT! :)
// Read returns a huge slice (allocates ~65 MB of memory)
func Read() []int {
return rand.Perm(2 << 22)
}
// Report cleans the memory and prints the current memory usage
func Report() {
var m runtime.MemStats
runtime.GC()
runtime.ReadMemStats(&m)
fmt.Printf(" > Memory Usage: %v KB\n", m.Alloc/1024)
}

View File

@ -0,0 +1,67 @@
// 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"
"io/ioutil"
"github.com/inancgumus/learngo/16-slices/exercises/24-fix-the-memory-leak/api"
)
// ---------------------------------------------------------
// EXERCISE: Fix the memory leak
//
// You're receiving millions of temperature data points from
// an API, but you only need the last 10 data points.
//
// Currently, there is a memory leak in your program.
// Find the leak and fix it.
//
//
// EXPECTED OUTPUT
//
// > Memory Usage: 116 KB
// > Memory Usage: 118 KB
//
//
// EXPECTED OUTPUT EXPLANATION
//
// Your output will be different. Your goal is to reduce the
// difference between the two measurements of the memory usage.
//
// For the expected output above:
//
// 118 KB - 116 KB = Only 2 KB so that's OK.
//
// However, in the current program, because of the memory leak,
// the difference is huge: about ~60 MB. Run the program and,
// see yourself.
//
// ---------------------------------------------------------
func main() {
// reports the initial memory usage
api.Report()
// reads 65 MB of temperature data into the memory!
temps := api.Read()
// -----------------------------------------------------
// ✪ ONLY ADD YOUR CODE INSIDE THIS BOX ✪
//
//
// ✪ ONLY ADD YOUR CODE INSIDE THIS BOX ✪
// -----------------------------------------------------
// fix the problem so that the memory usage stays low
// dont touch this code
api.Report()
fmt.Fprintln(ioutil.Discard, temps[0])
}

View File

@ -0,0 +1,24 @@
package api
import (
"fmt"
"math/rand"
"runtime"
)
// DO NOT TOUCH THE FOLLOWING CODE
// THIS IS THE API
// YOU CANNOT CONTROL IT! :)
// Read returns a huge slice (allocates ~65 MB of memory)
func Read() []int {
return rand.Perm(2 << 22)
}
// Report cleans the memory and prints the current memory usage
func Report() {
var m runtime.MemStats
runtime.GC()
runtime.ReadMemStats(&m)
fmt.Printf(" > Memory Usage: %v KB\n", m.Alloc/1024)
}

View File

@ -0,0 +1,45 @@
// 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"
"io/ioutil"
"github.com/inancgumus/learngo/16-slices/exercises/24-fix-the-memory-leak/solution/api"
)
func main() {
// reports the initial memory usage
api.Report()
// reads 65 MB of temperature data into the memory!
temps := api.Read()
//
// SOLUTION #1:
//
// clone the last 10 elements of the returned temperatures
// into a new slice
need := make([]int, 10)
copy(need, temps[len(temps)-10:])
// make the temp slice lose reference to its backing array
// so that it can be cleaned from the memory
temps = need
//
// SOLUTION #2:
//
// The code below does the same thing like the code above but in one line.
// temps = append([]int(nil), temps[len(temps)-10:]...)
api.Report()
fmt.Fprintln(ioutil.Discard, temps[0])
}

View File

@ -0,0 +1,119 @@
package main
import (
s "github.com/inancgumus/prettyslice"
)
// ---------------------------------------------------------
// EXERCISE: Add newlines to the lyric sentences
//
// You have a slice of lyrics data of Beatles' awesome
// song: Yesterday. Your goal is adding newlines after
// each sentence of the lyric.
//
// Your goal is creating a new slice, then copying
// each sentence of the lyric to the new buffer, then
// after each sentence adding a newline character.
//
// You cannot guess how many times you will get across
// something like this. Believe me, learning this will
// make you a better Gopher.
//
//
// RESTRICTIONS
//
// . For warming-up, in this exercise, never use the `append()` func.
//
// . Instead, only use the `copy()` func.
//
// . You cannot use slicing for printing the sentences.
//
// . Instead, use slicing to fill the new buffer, then print it.
//
//
// STEPS
//
// . Follow the instructions inside the code.
//
//
// EXPECTED OUTPUT
//
// yesterday all my troubles seemed so far away
// now it looks as though they are here to stay
// oh i believe in yesterday
//
// ---------------------------------------------------------
func main() {
//
// YOU DON'T NEED TO TOUCH THIS
//
// This inits some options for the prettyslice package.
// You can change the options if you want.
//
// s.Colors(false) // if your editor is light colored then enable this
s.PrintBacking = true // prints the backing arrays
s.MaxPerLine = 15 // prints max 15 elements per line
s.SpaceCharacter = "*" // print this instead of printing a newline (for debugging)
//
// UNCOMMENT THE VARIABLE BELOW THEN START!
//
// lyric := strings.Fields(`yesterday all my troubles seemed so far away now it looks as though they are here to stay oh i believe in yesterday`)
//
// RESTRICTION EXPLANATION:
//
// Don't do something like this:
// (Do not use slicing for printing the sentences)
//
// fmt.Println(lyric[:8])
// fmt.Println(lyric[8:18])
// fmt.Println(lyric[18:23])
// ========================================================================
//
// #1: CREATE A LARGE ENOUGH BUFFER (A NEW SLICE)
//
// You need to put each lyric sentence + a newline into this buffer
//
// I name the buffer: `fix`, you can name it however you want
// fix := make(...)
// ========================================================================
//
// #2: CALCULATE THE CUT POINTS
//
// You want to put newlines after each sentence. So you may want to put
// these index positions into a slice. Then you can use them to cut the
// lyric slice.
//
// cutpoints := []int{...}
// ========================================================================
//
// #3: CREATE A LOOP AND COPY THE SENTENCES INTO THE BUFFER
//
// for ... {
// Use the `copy` function to copy from the `lyric` slice to the buffer.
//
// Copy a newline character (in a string) to the buffer after each sentence.
//
// You can use slicing here for filling the new buffer.
//
// Uncomment this to aid debugging (to see how the fix slice changes)
// s.Show("fix slice", fix)
// }
// ========================================================================
//
// #4: PRINT THE BUFFER
//
// ...
}

View File

@ -0,0 +1,93 @@
// 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"
"strings"
s "github.com/inancgumus/prettyslice"
)
func main() {
//
// YOU DON'T NEED TO TOUCH THIS
//
// This inits some options for the prettyslice package.
// You can change the options if you want.
//
// s.Colors(false) // if your editor is light colored then enable this
s.PrintBacking = true // prints the backing arrays
s.MaxPerLine = 15 // prints max 15 elements per line
s.SpaceCharacter = "*" // print this instead of printing a newline (for debugging)
lyric := strings.Fields(`yesterday all my troubles seemed so far away now it looks as though they are here to stay oh i believe in yesterday`)
// CREATE A LARGE ENOUGH BUFFER
// `+3` because we're going to add 3 newline characters
fix := make([]string, len(lyric)+3)
// CALCULATE THE CUT POINTS
//
// + The first sentence has 8 words so its cutpoint is 8.
//
// + The second one has 10 words so its cutpoint is 10.
//
// + The third one has 5 words so its cutpoint is 5.
//
//
// yesterday all my troubles seemed so far away now it looks as though they are here to stay
// |
// v
// cutpoint: 8
//
// ... now it looks as though they are here to stay oh i believe in yesterday
// |
// v
// 10
//
// ... now it looks as though they are here to stay oh i believe in yesterday
// |
// v
// 5
cutpoints := []int{8, 10, 5}
// `n` tracks how much we've moved inside the `lyric` slice
// `i` tracks the sentence that we're on
for i, n := 0, 0; n < len(lyric); i++ {
//
// copy to `fix` from the `lyric`
//
// destination:
// fix[n+i] because we don't want to delete the previous copy.
// it moves sentence by sentence, using the cutpoints.
//
// source:
// lyric[n:n+cutpoints[i]] because we want copy the next sentence
// beginning from the number of the last copied elements to the
// n+next cutpoint (the next sentence).
//
n += copy(fix[n+i:], lyric[n:n+cutpoints[i]])
// add a newline after the number of copied elements
// notice that the '\n' position slides as we move over
// that's why n+i
fix[n+i] = "\n"
// uncomment this to aid debugging (to see how the fix slice changes)
// s.Show("fix slice", fix)
}
// print the fix slice
for _, w := range fix {
fmt.Print(w)
if w != "\n" {
fmt.Print(" ")
}
}
}

View File

@ -0,0 +1,104 @@
package main
// ---------------------------------------------------------
// EXERCISE: Print daily requests
//
// You've got a requests log for a system in a slice: `reqs`.
// The log contains the total requests counts per 8 hours.
//
// The reqs slice is a single-dimensional slice but you need
// to group the daily into a slice named: `daily`.
//
// Please follow the instructions inside the code to solve
// the exercise.
//
//
// EXPECTED OUTPUT
//
// Please run `solution/main.go` to see the expected
// output.
//
// go run solution/main.go
//
// ---------------------------------------------------------
func main() {
//
// #1: DAILY REQUESTS DATA
//
// . The system collects and groups the requests per 8 hours
//
// . So there are 3 requests totals per day
//
// . Your code should be robust enough to work with
// insufficient data. For example, in the last day
// there are only two request totals: 100 and 150.
//
// So, don't forget to handle that edge case as well.
//
// . Uncomment the code below and start
//
// reqs := []int{
// 500, 600, 250,
// 200, 400, 50,
// 900, 800, 600,
// 750, 250, 100,
// 100, 150,
// }
//
// #2: Group the `reqs` per day into a slice named: `daily`
//
// . Create the daily slice using the `make` function
//
// . Anticipate the length argument to the make function using this data:
//
// + The length of the reqs slice
// + There are 3 requests totals per day
// + There are residual elements (the last day)
//
// ! So, do not blindly allocate a slice.
//
// ! Allocate the slice efficiently with the exact size needed.
//
// . Then append to it the daily requests in a "loop"
//
//
// #3: Print the header:
//
// Day Requests
// ====================
//
//
// #4: Print the data per day along with the totals:
//
// 1 500
// 1 600
// 1 250
// --------------------
// 1350 --> Print the daily total requests
//
// 2 200
// 2 400
// 2 50
// --------------------
// 650
//
// 2000 --> Also print the grand total
// ------------------------------------------------------------------------
//
// ❤️ NOTE ❤️
//
// If you could't solve this challenge. Please do not get discouraged.
//
// Look at the solution, then try to solve it again. This is valuable too.
//
// Then change the request data, the number of requests per day (now it's 3), etc
// and then try to solve it again.
//
// ------------------------------------------------------------------------
}

View File

@ -0,0 +1,81 @@
// 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"
"strings"
)
func main() {
//
// DAILY REQUESTS DATA
//
reqs := []int{
500, 600, 250,
200, 400, 50,
900, 800, 600,
750, 250, 100,
100, 150,
}
//
// There are 3 requests per day
//
const N = 3
//
// Allocate the slice efficiently with the exact size needed
//
l := len(reqs)
size := l / N
if l%N != 0 {
size++
}
daily := make([][]int, 0, size)
//
// Group the `reqs` per day into a slice named: `daily`
//
for N < len(reqs) {
daily = append(daily, reqs[:N]) // add the current batch of nums to the `groups`
reqs = reqs[N:] // move the slice pointer for the next batch
}
//
// Add the residual elements to the group (len(reqs) % N)
//
daily = append(daily, reqs)
//
// Print the header:
//
fmt.Printf("%-10s%-10s\n", "Day", "Requests")
fmt.Println(strings.Repeat("=", 20))
//
// Print the data per day along with the totals:
//
var grand int
for i, d := range daily {
var sum int
for _, q := range d {
sum += q
fmt.Printf("%-10d%-10d\n", i+1, q)
}
fmt.Println(strings.Repeat("-", 20))
fmt.Printf("%10s%-10d\n\n", "", sum)
grand += sum
}
fmt.Printf("%10s%-10d\n", "", grand)
}

View File

@ -2,7 +2,7 @@
## Exercises Level I - Basics — Warm-Up
These are warm-up exercises that will reinforce your knowledge of slices.
Let's reinforce your basic knowledge of slices.
1. **[Declare nil slices](https://github.com/inancgumus/learngo/tree/master/16-slices/exercises/01-declare-nil)**
@ -20,6 +20,8 @@ These are warm-up exercises that will reinforce your knowledge of slices.
## Exercises Level II - Appending
Discover the power of the append function.
1. **[Append #1 — Append and compare byte slices](https://github.com/inancgumus/learngo/tree/master/16-slices/exercises/07-append)**
2. **[Append #2 — Append to a nil slice](https://github.com/inancgumus/learngo/tree/master/16-slices/exercises/08-append-2)**
@ -37,6 +39,8 @@ These are warm-up exercises that will reinforce your knowledge of slices.
## Exercises Level III - Slicing
Discover the power of slicing.
1. **[Slice the numbers](https://github.com/inancgumus/learngo/tree/master/16-slices/exercises/13-slicing-basics)**
2. **[Slicing by arguments](https://github.com/inancgumus/learngo/tree/master/16-slices/exercises/14-slicing-by-args)**
@ -47,6 +51,8 @@ These are warm-up exercises that will reinforce your knowledge of slices.
## Exercises Level IV - Internals
Peek into the internals of the slices and gain more insight. This is necessary for complete command of the slices.
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)**
@ -57,4 +63,30 @@ These are warm-up exercises that will reinforce your knowledge of slices.
5. **[Observe the capacity growth](https://github.com/inancgumus/learngo/tree/master/16-slices/exercises/20-observe-the-cap-growth)**
6. **[Correct the lyric](https://github.com/inancgumus/learngo/tree/master/16-slices/exercises/21-correct-the-lyric)**
6. **[Correct the lyric](https://github.com/inancgumus/learngo/tree/master/16-slices/exercises/21-correct-the-lyric)**
---
## Exercises Level V - Advanced Operations
Commonly used and more advanced operations are available to slices. Now, it's time to test yourself and fix some common problems.
1. **[Practice Advanced Slice Operations](https://github.com/inancgumus/learngo/tree/master/16-slices/exercises/22-adv-ops-practice)**
Let's warm you up for the advanced slice operations, and reinforce your neurons.
2. **[Limit the backing array sharing](https://github.com/inancgumus/learngo/tree/master/16-slices/exercises/23-limit-the-backing-array-sharing)**
Your API does not control the slices that it share with the outside world. You need to fix it.
3. **[Fix the Memory Leak](https://github.com/inancgumus/learngo/tree/master/16-slices/exercises/25-fix-the-memory-leak)**
A slice retrieved from an API causes a memory leak in your program. You need to fix it.
4. **[Add newlines to the lyric sentences](https://github.com/inancgumus/learngo/tree/master/16-slices/exercises/25-add-lines)**
Use the power of the `copy()` function and add newlines into a new buffer from a string slice. This exercise is more tricky than you might think.
5. **[Print Daily Requests](https://github.com/inancgumus/learngo/tree/master/16-slices/exercises/26-print-daily-requests)**
Group the requests log into a multi-dimensional from a single-dimensional slice. Allocate a slice with the exact size needed by doing some wizardary calculations. And lastly, pretty print the result.

View File

@ -25,8 +25,10 @@ func main() {
return
}
report()
img = append([]byte(nil), img[:24]...)
img = img[:24:24]
// img = img[:24:24] // unnecessary
report()
// s.PrintBacking = true
// s.MaxPerLine = 8

View File

@ -1,68 +0,0 @@
# Slice Exercises
---
# ANNOUNCEMENT
I teach you what the other courses don't even care to teach.
**What's new?**
* New Section: Advanced Slice Operations
* New Exercises for the Slice Internals
* New Exercises for the Slices: Advanced Operations
**What are you going to learn?**
* Full Slice Expressions: Limiting access to the backing array
* Make(): Preallocation
* Copy(): Efficiently and safely copy elements without using a loop
* Multi-Dimensional Slices
**What's coming next?**
* Empty Filer Finder: Your first taste of file operations.
* Bouncing Ball: Create a bouncing ball animation on a 2D surface.
* Png Parser: Parse a PNG file by hand and tell its dimensions.
These lectures will be added in the next 3 weeks.
**Statistics:**
* +1 hour of additional content!
* +5 new lectures
* +20 new questions
* 3 + ? new exercises
**Total content in the slices section:**
* ? hours
* ? lectures
* ? questions
* ? exercises
---
## Full Slice Exp + Make + Copy + Multi-Dim Slices
# FIX THIS
1. **[Limit the backing array sharing](https://github.com/inancgumus/learngo/tree/master/16-slices/exercises/??-limit-the-backing-array-sharing)**
* fix the excess memory allocation
* return a huge slice from a func, ask the student fix it
* full slice exp: https://play.golang.org/p/SPrLspRBXdI
* copy: https://play.golang.org/p/SPrLspRBXdI
* + put \n for the beatles exercise using copy
```go
s = append(s, 0 /* use the zero value of the element type */)
copy(s[i+1:], s[i:])
s[i] = x
```
* multi dim slices batches
```go
actions := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
batchSize := 3
var batches [][]int
for batchSize < len(actions) {
actions, batches = actions[batchSize:], append(batches, actions[0:batchSize:batchSize])
}
batches = append(batches, actions)
```

View File

@ -1,14 +0,0 @@
package main
// ---------------------------------------------------------
// EXERCISE: ?
//
//
//
// EXPECTED OUTPUT
//
//
// ---------------------------------------------------------
func main() {
}

View File

@ -1,11 +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
func main() {
}