Download and extract the code skeleton for this exercise.
A Struct for Points
Create a .go file points.go, then create a struct Point for two-dimensional points:
(x,y)values. The struct should have two fields, x and y, both float64.
Create a function NewPoint that creates a Point given x and y values: this would be a constructor in any other language, but in Go, it’s just a function that returns a Point. [Note: we don’t really need a constructor this simple and could use a Go struct literal instead, but we’re creating the constructor anyway. There are, of course, cases where some work is required to construct a struct instance.]
String Representation
There is a perfectly reasonable default string representation for structs in Go (which is used if you fmt.Print them), but we can make it nicer.
Create a String() method on Point using fmt.Sprintf to output the usual parentheses-and-commas representation of a point:
pt := NewPoint(3, 4.5)
fmt.Println(pt) // should print (3, 4.5)
fmt.Println(pt.String() == “(3, 4.5)”) // should print true
Hint: The %v format seems nice.
Go Get
The tests provided for this exercise use the assert package which is excellent, but not part of the standard library. It will give us a chance to see how easily Go can install external dependencies:
go get github.com/stretchr/testify/assert
Mutating Methods
Our Point class was effectively immutable (from outside the package) since there were no exported members, and not methods that changed its state.
Let’s add some methods that change the Point in place. In order to modify the struct, a method must use a pointer receiver argument. Feel free to copy your Point implementation from last week, and in points.go, change the package to exer9 and…
Scale
Add a .Scale(f) method to the Point from last week. It should change an existing point in place and stretch it by a factor of f. For example:
p := Point{1, 2}
p.Scale(5)
fmt.Println(p)
… should print (5, 10).
Rotate
We can add another transformation to a Point: rotation. We would like to call .Rotate(angle) and have the point rotated by angle radians around the origin. For angle a, the transformation is:
The result should be that this code:
p := Point{1, 0}
p.Rotate(math.Pi / 2)
fmt.Println(p)
p.Rotate(math.Pi / 2)
fmt.Println(p)
… should print (0, 1) and (-1, 0) (but probably not exactly due to floating-point error, but the results will be close to these values).
Random Arrays
We want to generate some random arrays. In array_stats.go, write a function that creates and returns an array with length integers and values from 0 to maxInt-1 with this signature:
func RandomArray(length int, maxInt int) []int { … }
When doing this, create a new random number generator that has a reasonable initial seed.
Array Summary Stats
We want to calculate the mean and standard deviation of values in an array/slice. For arrays with value x and n elements, we will use these formulas:
In order to calculate these, we need the length of the slice (easy),
Those are things we can calculate in parallel on a large array.
We will break the array into some evenly-sized slices and want to calculate the two sums on each of the chunks concurrently in goroutines. Our function will take the array/slice and number of chunks to break the array into:
func MeanStddev(arr []int, chunks int) (mean, stddev float64) { … }
You can assume that the length of arr is divisible by chunks: no rounding error to worry about.
In order to do this, you need to create:
- A struct to hold partial sums as they are generated.
- A channel to communicate these partial sums back to the MeanStddev function.
- A function that can run in a goroutine, calculate the sum and sum of squares on a slice, and send the values back along the channel.
In MeanStddev, you will need to create chunks goroutines to calculate partial sums. Then receive chunks values from the channel, create the final sums, and return the correct values.