New examples
This commit is contained in:
parent
4e0db8f6a3
commit
4a80c6fd87
17 changed files with 1101 additions and 0 deletions
30
chessboard/.exercism/config.json
Normal file
30
chessboard/.exercism/config.json
Normal file
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"authors": [
|
||||
"brugnara",
|
||||
"tehsphinx"
|
||||
],
|
||||
"contributors": [
|
||||
"eklatzer"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"chessboard.go"
|
||||
],
|
||||
"test": [
|
||||
"chessboard_test.go"
|
||||
],
|
||||
"exemplar": [
|
||||
".meta/exemplar.go"
|
||||
],
|
||||
"invalidator": [
|
||||
"go.mod"
|
||||
]
|
||||
},
|
||||
"forked_from": [
|
||||
"elixir/chessboard"
|
||||
],
|
||||
"blurb": "Learn about iterating ranges by generating a chessboard.",
|
||||
"custom": {
|
||||
"taskIdsEnabled": true
|
||||
}
|
||||
}
|
1
chessboard/.exercism/metadata.json
Normal file
1
chessboard/.exercism/metadata.json
Normal file
|
@ -0,0 +1 @@
|
|||
{"track":"go","exercise":"chessboard","id":"cead5ef4f6974272b7dfe12e6eb6f33f","url":"https://exercism.org/tracks/go/exercises/chessboard","handle":"snowyforest","is_requester":true,"auto_approve":false}
|
41
chessboard/HELP.md
Normal file
41
chessboard/HELP.md
Normal file
|
@ -0,0 +1,41 @@
|
|||
# Help
|
||||
|
||||
## Running the tests
|
||||
|
||||
To run the tests run the command `go test` from within the exercise directory.
|
||||
|
||||
If the test suite contains benchmarks, you can run these with the `--bench` and `--benchmem`
|
||||
flags:
|
||||
|
||||
go test -v --bench . --benchmem
|
||||
|
||||
Keep in mind that each reviewer will run benchmarks on a different machine, with
|
||||
different specs, so the results from these benchmark tests may vary.
|
||||
|
||||
## Submitting your solution
|
||||
|
||||
You can submit your solution using the `exercism submit chessboard.go` command.
|
||||
This command will upload your solution to the Exercism website and print the solution page's URL.
|
||||
|
||||
It's possible to submit an incomplete solution which allows you to:
|
||||
|
||||
- See how others have completed the exercise
|
||||
- Request help from a mentor
|
||||
|
||||
## Need to get help?
|
||||
|
||||
If you'd like help solving the exercise, check the following pages:
|
||||
|
||||
- The [Go track's documentation](https://exercism.org/docs/tracks/go)
|
||||
- The [Go track's programming category on the forum](https://forum.exercism.org/c/programming/go)
|
||||
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
|
||||
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
|
||||
|
||||
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
|
||||
|
||||
To get help if you're having trouble, you can use one of the following resources:
|
||||
|
||||
- [How to Write Go Code](https://golang.org/doc/code.html)
|
||||
- [Effective Go](https://golang.org/doc/effective_go.html)
|
||||
- [Go Resources](http://golang.org/help)
|
||||
- [StackOverflow](http://stackoverflow.com/questions/tagged/go)
|
34
chessboard/HINTS.md
Normal file
34
chessboard/HINTS.md
Normal file
|
@ -0,0 +1,34 @@
|
|||
# Hints
|
||||
|
||||
## General
|
||||
|
||||
- An [integer value][integers] can be defined as one or more consecutive digits.
|
||||
- A [map value][maps] stores key-value data
|
||||
|
||||
## 1. Given a Chessboard and a File, count how many squares are occupied
|
||||
|
||||
- You can iterate a [map][maps]
|
||||
- Check if the value is true. If it is increment. This is to count pieces.
|
||||
- You have to [explicitly return an integer][return] from a function.
|
||||
|
||||
## 2. Given a Chessboard and a Rank, count how many squares are occupied
|
||||
|
||||
- You'll first need to check the rank is within range.
|
||||
- Loop over the chessboard.
|
||||
- Add one if the square is occupied.
|
||||
|
||||
## 3. Count how many squares are present in the given chessboard
|
||||
|
||||
- There are many ways to solve this.
|
||||
- This should return how many squares are configured in a chess-board.
|
||||
|
||||
## 4. Count how many squares are occupied in the given chessboard
|
||||
|
||||
- Get the CountInFile for all files in the chessboard.
|
||||
|
||||
[functions]: https://golang.org/ref/spec#Function_declarations
|
||||
[return]: https://golang.org/ref/spec#Return_statements
|
||||
[operators]: https://golang.org/ref/spec#Operators
|
||||
[integers]: https://golang.org/ref/spec#Integer_literals
|
||||
[calls]: https://golang.org/ref/spec#Calls
|
||||
[maps]: /tracks/go/concepts/maps
|
199
chessboard/README.md
Normal file
199
chessboard/README.md
Normal file
|
@ -0,0 +1,199 @@
|
|||
# Chessboard
|
||||
|
||||
Welcome to Chessboard on Exercism's Go Track.
|
||||
If you need help running the tests or submitting your code, check out `HELP.md`.
|
||||
If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :)
|
||||
|
||||
## Introduction
|
||||
|
||||
In Go, you can iterate over a `slice` using `for` and an index, or you can use `range`.
|
||||
`range` also allows you to iterate over a `map`.
|
||||
|
||||
Every iteration returns two values: the index/key and a copy of the element at that index/key.
|
||||
|
||||
## Iterate over a slice
|
||||
|
||||
Easy as pie, loops over a slice, ordered as expected.
|
||||
|
||||
```go
|
||||
xi := []int{10, 20, 30}
|
||||
for i, x := range xi {
|
||||
fmt.Println(i, x)
|
||||
}
|
||||
// outputs:
|
||||
// 0, 10
|
||||
// 1, 20
|
||||
// 2, 30
|
||||
```
|
||||
|
||||
## Iterate over a map
|
||||
|
||||
Iterating over a map raises a new problem. The order is now random.
|
||||
|
||||
```go
|
||||
hash := map[int]int{9: 10, 99: 20, 999: 30}
|
||||
for k, v := range hash {
|
||||
fmt.Println(k, v)
|
||||
}
|
||||
// outputs, for example:
|
||||
// 99 20
|
||||
// 999 30
|
||||
// 9 10
|
||||
```
|
||||
|
||||
~~~~exercism/note
|
||||
It may seem the above output is incorrect, as one would expect the first key/value pair on the declaration of the map `9 10` to be the first one printed and not the last.
|
||||
However, maps are unordered by nature - there isn't a first or last key/value pair.
|
||||
Because of that, when iterating over the entries of a map, the order by which entries will be visited will be random and not follow any specific pattern.
|
||||
This means the above output is possible but might differ from what you get if you try to run this yourself.
|
||||
To learn more about this see [Go Language Spec: range clause](https://go.dev/ref/spec#RangeClause).
|
||||
~~~~
|
||||
|
||||
## Iteration omitting key or value
|
||||
|
||||
In Go an unused variable will raise an error at build time.
|
||||
Sometimes you only need the value, as per the first example:
|
||||
|
||||
```go
|
||||
xi := []int{10, 20, 30}
|
||||
for i, x := range xi {
|
||||
fmt.Println(x)
|
||||
}
|
||||
// Go build failed: i declared but not used
|
||||
```
|
||||
|
||||
You can replace the `i` with `_` which tells the compiler we don't use that value:
|
||||
|
||||
```go
|
||||
xi := []int{10, 20, 30}
|
||||
for _, x := range xi {
|
||||
fmt.Println(x)
|
||||
}
|
||||
// outputs:
|
||||
// 10
|
||||
// 20
|
||||
// 30
|
||||
```
|
||||
|
||||
If you want to only print the index, you can replace the `x` with `_`,
|
||||
or simply omit the declaration:
|
||||
|
||||
```go
|
||||
xi := []int{10, 20, 30}
|
||||
// for i, _ := range xi {
|
||||
for i := range xi {
|
||||
fmt.Println(i)
|
||||
}
|
||||
// outputs:
|
||||
// 0
|
||||
// 1
|
||||
// 2
|
||||
```
|
||||
|
||||
## Non-struct types
|
||||
|
||||
You've previously seen defining struct types.
|
||||
It is also possible to define non-struct types which you can use as an alias for a built-in type declaration, and you can define receiver functions on them to extend them in the same way as struct types.
|
||||
|
||||
```go
|
||||
type Name string
|
||||
func SayHello(n Name) {
|
||||
fmt.Printf("Hello %s\n", n)
|
||||
}
|
||||
n := Name("Fred")
|
||||
SayHello(n)
|
||||
// Output: Hello Fred
|
||||
```
|
||||
|
||||
You can also define non-struct types composed of arrays and maps.
|
||||
|
||||
```go
|
||||
type Names []string
|
||||
func SayHello(n Names) {
|
||||
for _, name := range n {
|
||||
fmt.Printf("Hello %s\n", name)
|
||||
}
|
||||
}
|
||||
n := Names([]string{"Fred", "Bill"})
|
||||
SayHello(n)
|
||||
// Output:
|
||||
// Hello Fred
|
||||
// Hello Bill
|
||||
```
|
||||
|
||||
## Instructions
|
||||
|
||||
As a chess enthusiast, you would like to write your own version of the game. Yes, there may be plenty of implementations of chess available online already, but yours will be unique!
|
||||
|
||||
Each square of the chessboard is identified by a letter-number pair:
|
||||
- The horizontal rows of squares, called ranks, are numbered 1 through 8.
|
||||
- The vertical columns of squares, called files, are labeled A through H.
|
||||
|
||||
```
|
||||
A B C D E F G H
|
||||
8 # _ _ _ # _ _ # 8
|
||||
7 _ _ _ _ _ _ _ _ 7
|
||||
6 _ _ _ _ # _ _ # 6
|
||||
5 _ # _ _ _ _ _ # 5
|
||||
4 _ _ _ _ _ _ # # 4
|
||||
3 # _ # _ _ _ _ # 3
|
||||
2 _ _ _ _ _ _ _ # 2
|
||||
1 # _ _ _ _ _ _ # 1
|
||||
A B C D E F G H
|
||||
```
|
||||
|
||||
## 1. Given a Chessboard and a File, count how many squares are occupied
|
||||
|
||||
Implement the `CountInFile(board Chessboard, file string) int` function.
|
||||
It should count the total number of occupied squares by ranging over a map. Return an integer.
|
||||
Return a count of zero (`0`) if the given file cannot be found in the map.
|
||||
|
||||
```go
|
||||
CountInFile(board, "A")
|
||||
// => 3
|
||||
```
|
||||
|
||||
## 2. Given a Chessboard and a Rank, count how many squares are occupied
|
||||
|
||||
Implement the `CountInRank(board Chessboard, rank int) int` function.
|
||||
It should count the total number of occupied squares by ranging over the given rank. Return an integer.
|
||||
Return a count of zero (`0`) if the given rank is not a valid one (not between `1` and `8`, inclusive).
|
||||
|
||||
```go
|
||||
CountInRank(board, 2)
|
||||
// => 1
|
||||
```
|
||||
|
||||
## 3. Count how many squares are present in the given chessboard
|
||||
|
||||
Implement the `CountAll(board Chessboard) int` function.
|
||||
It should count how many squares are present in the chessboard and returns
|
||||
an integer. Since you don't need to check the content of the squares,
|
||||
consider using range omitting both `index` and `value`.
|
||||
|
||||
```go
|
||||
CountAll(board)
|
||||
// => 64
|
||||
```
|
||||
|
||||
## 4. Count how many squares are occupied in the given chessboard
|
||||
|
||||
Implement the `CountOccupied(board Chessboard) int` function.
|
||||
It should count how many squares are occupied in the chessboard.
|
||||
Return an integer.
|
||||
|
||||
```go
|
||||
CountOccupied(board)
|
||||
// => 15
|
||||
```
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @brugnara
|
||||
- @tehsphinx
|
||||
|
||||
### Contributed to by
|
||||
|
||||
- @eklatzer
|
27
chessboard/chessboard.go
Normal file
27
chessboard/chessboard.go
Normal file
|
@ -0,0 +1,27 @@
|
|||
package chessboard
|
||||
|
||||
// Declare a type named File which stores if a square is occupied by a piece - this will be a slice of bools
|
||||
|
||||
// Declare a type named Chessboard which contains a map of eight Files, accessed with keys from "A" to "H"
|
||||
|
||||
// CountInFile returns how many squares are occupied in the chessboard,
|
||||
// within the given file.
|
||||
func CountInFile(cb Chessboard, file string) int {
|
||||
panic("Please implement CountInFile()")
|
||||
}
|
||||
|
||||
// CountInRank returns how many squares are occupied in the chessboard,
|
||||
// within the given rank.
|
||||
func CountInRank(cb Chessboard, rank int) int {
|
||||
panic("Please implement CountInRank()")
|
||||
}
|
||||
|
||||
// CountAll should count how many squares are present in the chessboard.
|
||||
func CountAll(cb Chessboard) int {
|
||||
panic("Please implement CountAll()")
|
||||
}
|
||||
|
||||
// CountOccupied returns how many squares are occupied in the chessboard.
|
||||
func CountOccupied(cb Chessboard) int {
|
||||
panic("Please implement CountOccupied()")
|
||||
}
|
102
chessboard/chessboard_test.go
Normal file
102
chessboard/chessboard_test.go
Normal file
|
@ -0,0 +1,102 @@
|
|||
package chessboard
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// newChessboard return a *Chessboard for tests
|
||||
//
|
||||
// A B C D E F G H
|
||||
// 8 # _ _ _ # _ _ # 8
|
||||
// 7 _ _ _ _ _ _ _ _ 7
|
||||
// 6 _ _ _ _ # _ _ # 6
|
||||
// 5 _ # _ _ _ _ _ # 5
|
||||
// 4 _ _ _ _ _ _ # # 4
|
||||
// 3 # _ # _ _ _ _ # 3
|
||||
// 2 _ _ _ _ _ _ _ # 2
|
||||
// 1 # _ _ _ _ _ _ # 1
|
||||
// A B C D E F G H
|
||||
|
||||
func newChessboard() Chessboard {
|
||||
return Chessboard{
|
||||
"A": File{true, false, true, false, false, false, false, true},
|
||||
"B": File{false, false, false, false, true, false, false, false},
|
||||
"C": File{false, false, true, false, false, false, false, false},
|
||||
"D": File{false, false, false, false, false, false, false, false},
|
||||
"E": File{false, false, false, false, false, true, false, true},
|
||||
"F": File{false, false, false, false, false, false, false, false},
|
||||
"G": File{false, false, false, true, false, false, false, false},
|
||||
"H": File{true, true, true, true, true, true, false, true},
|
||||
}
|
||||
}
|
||||
|
||||
func TestCountInFile(t *testing.T) {
|
||||
cb := newChessboard()
|
||||
testCases := []struct {
|
||||
in string
|
||||
expected int
|
||||
}{
|
||||
{in: "A", expected: 3},
|
||||
{in: "B", expected: 1},
|
||||
{in: "C", expected: 1},
|
||||
{in: "D", expected: 0},
|
||||
{in: "E", expected: 2},
|
||||
{in: "F", expected: 0},
|
||||
{in: "G", expected: 1},
|
||||
{in: "H", expected: 7},
|
||||
{in: "Z", expected: 0},
|
||||
}
|
||||
for _, test := range testCases {
|
||||
t.Run(fmt.Sprintf("Count of occupied squares in file %s", test.in), func(t *testing.T) {
|
||||
if got := CountInFile(cb, test.in); got != test.expected {
|
||||
t.Errorf("CountInFile(chessboard, %q) = %d, want: %d", test.in, got, test.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCountInRank(t *testing.T) {
|
||||
cb := newChessboard()
|
||||
testCases := []struct {
|
||||
in int
|
||||
expected int
|
||||
}{
|
||||
{in: 1, expected: 2},
|
||||
{in: 2, expected: 1},
|
||||
{in: 3, expected: 3},
|
||||
{in: 4, expected: 2},
|
||||
{in: 5, expected: 2},
|
||||
{in: 6, expected: 2},
|
||||
{in: 7, expected: 0},
|
||||
{in: 8, expected: 3},
|
||||
// cases not between 1 and 8, inclusive
|
||||
{in: 100, expected: 0},
|
||||
{in: 0, expected: 0},
|
||||
{in: -1, expected: 0},
|
||||
{in: -100, expected: 0},
|
||||
}
|
||||
for _, test := range testCases {
|
||||
t.Run(fmt.Sprintf("Count of occupied squares in rank %d", test.in), func(t *testing.T) {
|
||||
if got := CountInRank(cb, test.in); got != test.expected {
|
||||
t.Errorf("CountInRank(chessboard, %d) = %d, want: %d", test.in, got, test.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCountAll(t *testing.T) {
|
||||
cb := newChessboard()
|
||||
expected := 64
|
||||
if got := CountAll(cb); got != expected {
|
||||
t.Errorf("CountAll(chessboard) = %d, want: %d", got, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCountOccupied(t *testing.T) {
|
||||
cb := newChessboard()
|
||||
expected := 15
|
||||
if got := CountOccupied(cb); got != expected {
|
||||
t.Errorf("CountOccupied(chessboard) = %d, want: %d", got, expected)
|
||||
}
|
||||
}
|
3
chessboard/go.mod
Normal file
3
chessboard/go.mod
Normal file
|
@ -0,0 +1,3 @@
|
|||
module chessboard
|
||||
|
||||
go 1.18
|
26
election-day/.exercism/config.json
Normal file
26
election-day/.exercism/config.json
Normal file
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"authors": [
|
||||
"andrerfcsantos"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"election_day.go"
|
||||
],
|
||||
"test": [
|
||||
"election_day_test.go"
|
||||
],
|
||||
"exemplar": [
|
||||
".meta/exemplar.go"
|
||||
],
|
||||
"editor": [
|
||||
"election_result.go"
|
||||
],
|
||||
"invalidator": [
|
||||
"go.mod"
|
||||
]
|
||||
},
|
||||
"blurb": "Learn about pointers by creating a simple voting system.",
|
||||
"custom": {
|
||||
"taskIdsEnabled": true
|
||||
}
|
||||
}
|
1
election-day/.exercism/metadata.json
Normal file
1
election-day/.exercism/metadata.json
Normal file
|
@ -0,0 +1 @@
|
|||
{"track":"go","exercise":"election-day","id":"a6a36b106cfd48819df4a4e23dbfa1dc","url":"https://exercism.org/tracks/go/exercises/election-day","handle":"snowyforest","is_requester":true,"auto_approve":false}
|
41
election-day/HELP.md
Normal file
41
election-day/HELP.md
Normal file
|
@ -0,0 +1,41 @@
|
|||
# Help
|
||||
|
||||
## Running the tests
|
||||
|
||||
To run the tests run the command `go test` from within the exercise directory.
|
||||
|
||||
If the test suite contains benchmarks, you can run these with the `--bench` and `--benchmem`
|
||||
flags:
|
||||
|
||||
go test -v --bench . --benchmem
|
||||
|
||||
Keep in mind that each reviewer will run benchmarks on a different machine, with
|
||||
different specs, so the results from these benchmark tests may vary.
|
||||
|
||||
## Submitting your solution
|
||||
|
||||
You can submit your solution using the `exercism submit election_day.go` command.
|
||||
This command will upload your solution to the Exercism website and print the solution page's URL.
|
||||
|
||||
It's possible to submit an incomplete solution which allows you to:
|
||||
|
||||
- See how others have completed the exercise
|
||||
- Request help from a mentor
|
||||
|
||||
## Need to get help?
|
||||
|
||||
If you'd like help solving the exercise, check the following pages:
|
||||
|
||||
- The [Go track's documentation](https://exercism.org/docs/tracks/go)
|
||||
- The [Go track's programming category on the forum](https://forum.exercism.org/c/programming/go)
|
||||
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
|
||||
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
|
||||
|
||||
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
|
||||
|
||||
To get help if you're having trouble, you can use one of the following resources:
|
||||
|
||||
- [How to Write Go Code](https://golang.org/doc/code.html)
|
||||
- [Effective Go](https://golang.org/doc/effective_go.html)
|
||||
- [Go Resources](http://golang.org/help)
|
||||
- [StackOverflow](http://stackoverflow.com/questions/tagged/go)
|
54
election-day/HINTS.md
Normal file
54
election-day/HINTS.md
Normal file
|
@ -0,0 +1,54 @@
|
|||
# Hints
|
||||
|
||||
## General
|
||||
|
||||
- `*T` can be used to declared variables that are pointers to some type `T`, e.g `var i *int` declares a variable `i` that is a pointer to an `int`
|
||||
- You can get a pointer for a variable (its memory address) by using the `&` operator, e.g `mypointer := &anIntVariable`.
|
||||
- You can get the value stored in a pointer by using the `*` operator on a pointer, e.g. `var i int = *aPointerToInt`. This is called dereferencing the pointer.
|
||||
- You check if a pointer is not `nil` before dereferencing it. Attempting to dereference a `nil` pointer will give you a runtime error.
|
||||
- If you are unsure how pointers work, try reading [Tour of Go: Pointers][go-tour-pointers] or [Go by Example: Pointers][go-by-example-pointers]
|
||||
|
||||
## 1. Create a vote counter
|
||||
|
||||
- You need to create a pointer to an `int`, in other words, a `*int`.
|
||||
- You can use the `&` operator on a variable to create a pointer to it, e.g `&myInt`
|
||||
- You can create a pointer to a new variable defined by you or you can use the variable of the function argument
|
||||
|
||||
## 2. Get number of votes from a counter
|
||||
|
||||
- You can use the `*` operator on a pointer to dereference it and get its value, e.g `*myPointer`
|
||||
- Dereferencing `nil` pointers will give you a runtime error. Always make sure a pointer is not `nil` before dereferencing it.
|
||||
|
||||
## 3. Increment the votes of a counter
|
||||
|
||||
- If you have a pointer `var myPointer *int`, you can assign to `*myPointer` to change the value pointed by `myPointer`
|
||||
- To get the current value of the pointer, you need to dereference it using the `*` operator, or call the function you made in the previous task.
|
||||
|
||||
## 4. Create the election results
|
||||
|
||||
- Create a new `ElectionResult` literal or variable with the fields `Name` and `Votes` filled with the values in the arguments of the function.
|
||||
- You can create a pointer from a variable or literal by using the `&` operator before the variable name/literal declaration, e.g `&myVariable` or `&ElectionResult{Name: "John", Votes: 1}`
|
||||
|
||||
## 5. Announce the results
|
||||
|
||||
- Although you are receiving a pointer to an `ElectionResult`, you can access its fields with the dot `.` notation, like if it wasn't a pointer!
|
||||
- Build the message by accessing the `Name` and `Value` fields on the struct.
|
||||
- Even though you are accessing fields from a pointer to a struct, you don't need to do any dereferencing. Go will automatically dereference the pointer for you, like in this example:
|
||||
|
||||
```go
|
||||
result := &ElectionResult{
|
||||
Name: "John",
|
||||
Votes: 32
|
||||
}
|
||||
|
||||
result.Name // "John" - Go will automatically dereference the pointer
|
||||
// and access the 'Name' field of the dereferenced struct
|
||||
```
|
||||
|
||||
## 6. Vote recounting
|
||||
|
||||
- You can think of maps as being pointers already. This means that changes you make to the map inside the function will be visible outside the function.
|
||||
- To increment the value of a key in a `var m map[string]int`, you have several options: `m["mykey"] = m["mykey"] + 1 `, `m["mykey"] += 1 ` or `m["mykey"]++ `
|
||||
|
||||
[go-tour-pointers]: https://tour.golang.org/moretypes/1
|
||||
[go-by-example-pointers]: https://gobyexample.com/pointers
|
301
election-day/README.md
Normal file
301
election-day/README.md
Normal file
|
@ -0,0 +1,301 @@
|
|||
# Election Day
|
||||
|
||||
Welcome to Election Day on Exercism's Go Track.
|
||||
If you need help running the tests or submitting your code, check out `HELP.md`.
|
||||
If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :)
|
||||
|
||||
## Introduction
|
||||
|
||||
Like many other languages, Go has pointers.
|
||||
If you're new to pointers, they can feel a little mysterious but once you get used to them, they're quite straight-forward.
|
||||
They're a crucial part of Go, so take some time to really understand them.
|
||||
|
||||
Before digging into the details, it's worth understanding the use of pointers. Pointers are a way to share memory with other parts of our program, which is useful for two major reasons:
|
||||
1. When we have large amounts of data, making copies to pass between functions is very inefficient.
|
||||
By passing the memory location of where the data is stored instead, we can dramatically reduce the resource-footprint of our programs.
|
||||
2. By passing pointers between functions, we can access and modify the single copy of the data directly, meaning that any changes made by one function are immediately visible to other parts of the program when the function ends.
|
||||
|
||||
## Variables and Memory
|
||||
|
||||
Let's say we have a regular integer variable `a`:
|
||||
|
||||
```go
|
||||
var a int
|
||||
```
|
||||
|
||||
When we declare a variable, Go has to find a place in memory to store its value. This is largely abstracted from us — when we need to fetch the value stored in that piece of memory, we can just refer to it by the variable name.
|
||||
|
||||
For instance, when we write `a + 2`, we are effectively fetching the value stored in the memory associated with the variable `a` and adding 2 to it.
|
||||
|
||||
Similarly, when we need to change the value in the piece of memory of `a`, we can use the variable name to do an assignment:
|
||||
|
||||
```go
|
||||
a = 3
|
||||
```
|
||||
|
||||
The piece of memory that is associated with `a` will now be storing the value `3`.
|
||||
|
||||
## Pointers
|
||||
|
||||
While variables allow us to refer to values in memory, sometimes it's useful to know the **memory address** to which the variable is pointing. **Pointers** hold the memory addresses of those values. You declare a variable with a pointer type by prefixing the underlying type with an asterisk:
|
||||
|
||||
```go
|
||||
var p *int // 'p' contains the memory address of an integer
|
||||
```
|
||||
|
||||
Here we declare a variable `p` of type "pointer to int" (`*int`). This means that `p` will hold the memory address of an integer. The zero value of pointers is `nil` because a `nil` pointer holds no memory address.
|
||||
|
||||
### Getting a pointer to a variable
|
||||
|
||||
To find the memory address of the value of a variable, we can use the `&` operator.
|
||||
For example, if we want to find and store the memory address of variable `a` in the pointer `p`, we can do the following:
|
||||
|
||||
```go
|
||||
var a int
|
||||
a = 2
|
||||
|
||||
var p *int
|
||||
p = &a // the variable 'p' contains the memory address of 'a'
|
||||
```
|
||||
|
||||
### Accessing the value via a pointer (dereferencing)
|
||||
|
||||
When we have a pointer, we might want to know the value stored in the memory address the pointer represents. We can do this using the `*` operator:
|
||||
|
||||
```go
|
||||
var a int
|
||||
a = 2
|
||||
|
||||
var p *int
|
||||
p = &a // the variable 'p' contains the memory address of 'a'
|
||||
|
||||
var b int
|
||||
b = *p // b == 2
|
||||
```
|
||||
|
||||
The operation `*p` fetches the value stored at the memory address stored in `p`. This operation is often called "dereferencing".
|
||||
|
||||
We can also use the dereference operator to assign a new value to the memory address referenced by the pointer:
|
||||
|
||||
```go
|
||||
var a int // declare int variable 'a'
|
||||
a = 2 // assign 'a' the value of 2
|
||||
|
||||
var pa *int
|
||||
pa = &a // 'pa' now contains to the memory address of 'a'
|
||||
*pa = *pa + 2 // increment by 2 the value at memory address 'pa'
|
||||
|
||||
fmt.Println(a) // Output: 4
|
||||
// 'a' will have the new value that was changed via the pointer!
|
||||
```
|
||||
|
||||
Assigning to `*pa` will change the value stored at the memory address `pa` holds. Since `pa` holds the memory address of `a`, by assigning to `*pa` we are effectively changing the value of `a`!
|
||||
|
||||
A note of caution however: always check if a pointer is not `nil` before dereferencing. Dereferencing a `nil` pointer will make the program crash at runtime!
|
||||
|
||||
```go
|
||||
var p *int // p is nil initially
|
||||
fmt.Println(*p)
|
||||
// panic: runtime error: invalid memory address or nil pointer dereference
|
||||
```
|
||||
|
||||
### Pointers to structs
|
||||
|
||||
So far we've only seen pointers to primitive values. We can also create pointers for structs:
|
||||
|
||||
```go
|
||||
type Person struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
|
||||
var peter Person
|
||||
peter = Person{Name: "Peter", Age: 22}
|
||||
|
||||
var p *Person
|
||||
p = &peter
|
||||
```
|
||||
|
||||
We could have also created a new `Person` and immediately stored a pointer to it:
|
||||
|
||||
```go
|
||||
var p *Person
|
||||
p = &Person{Name: "Peter", Age: 22}
|
||||
```
|
||||
|
||||
When we have a pointer to a struct, we don't need to dereference the pointer before accessing one of the fields:
|
||||
|
||||
```go
|
||||
var p *Person
|
||||
p = &Person{Name: "Peter", Age: 22}
|
||||
|
||||
fmt.Println(p.Name) // Output: "Peter"
|
||||
// Go automatically dereferences 'p' to allow
|
||||
// access to the 'Name' field
|
||||
```
|
||||
|
||||
## Slices and maps are already pointers
|
||||
|
||||
Slices and maps are special types because they already have pointers in their implementation. This means that more often than not, we don't need to create pointers for these types to share the memory address for their values. Imagine we have a function that increments the value of a key in a map:
|
||||
|
||||
|
||||
```go
|
||||
func incrementPeterAge(m map[string]int) {
|
||||
m["Peter"] += 1
|
||||
}
|
||||
```
|
||||
|
||||
If we create a map and call this function, the changes the function made to the map persist after the function ended. This is a similar behavior we get if we were using a pointer, but note how on this example we are not using any referencing/dereferencing or any of the pointer syntax:
|
||||
|
||||
```go
|
||||
ages := map[string]int{
|
||||
"Peter": 21
|
||||
}
|
||||
incrementPeterAge(ages)
|
||||
fmt.Println(ages)
|
||||
// Output: map[Peter:22]
|
||||
// The changes the function 'incrementPeterAge' made to the map are visible after the function ends!
|
||||
```
|
||||
|
||||
The same applies when changing an existing item in a slice.
|
||||
|
||||
However, actions that return a new slice like `append` are a special case and **might not** modify the slice outside of the function. This is due to the way slices work internally, but we won't cover this in detail in this exercise, as this is a more advanced topic. If you are really curious you can read more about this in [Go Blog: Mechanics of 'append'][mechanics-of-append]
|
||||
|
||||
[mechanics-of-append]: https://go.dev/blog/slices
|
||||
|
||||
## Instructions
|
||||
|
||||
A local school near you has a very active students' association.
|
||||
The students' association is managed by a president and once every 2 years,
|
||||
elections are run to elect a new president.
|
||||
|
||||
In this year's election, it was decided that a new digital system to
|
||||
count the votes was needed. The school needs your help building this new system.
|
||||
|
||||
## 1. Create a vote counter
|
||||
|
||||
One of the first things that the new voting system needs is a vote counter.
|
||||
This counter is a way to keep track of the votes a particular candidate has.
|
||||
|
||||
Create a function `NewVoteCounter` that accepts the number of initial votes for a candidate and returns a pointer referring to an `int`, initialized with the given number of initial votes.
|
||||
|
||||
```go
|
||||
var initialVotes int
|
||||
initialVotes = 2
|
||||
|
||||
var counter *int
|
||||
counter = NewVoteCounter(initialVotes)
|
||||
*counter == initialVotes // true
|
||||
```
|
||||
|
||||
## 2. Get number of votes from a counter
|
||||
|
||||
You now have a way to create new counters! But now you realize the new system will also need a way to get the number of votes from a counter.
|
||||
|
||||
Create a function `VoteCount` that will take a counter (`*int`) as an argument and will return the number of votes in the counter. If the counter is `nil` you should assume the counter has no votes:
|
||||
|
||||
```go
|
||||
var votes int
|
||||
votes = 3
|
||||
|
||||
var voteCounter *int
|
||||
voteCounter = &votes
|
||||
|
||||
VoteCount(voteCounter)
|
||||
// => 3
|
||||
|
||||
var nilVoteCounter *int
|
||||
VoteCount(nilVoteCounter)
|
||||
// => 0
|
||||
```
|
||||
|
||||
## 3. Increment the votes of a counter
|
||||
|
||||
It's finally the time to count the votes! Now you need a way to increment the votes in a counter.
|
||||
|
||||
Create a function `IncrementVoteCount` that will take a counter (`*int`) as an argument and a number of votes, and will increment the counter by that number of votes. You can assume the pointer passed will never be `nil`.
|
||||
|
||||
```go
|
||||
var votes int
|
||||
votes = 3
|
||||
|
||||
var voteCounter *int
|
||||
voteCounter = &votes
|
||||
|
||||
IncrementVoteCount(voteCounter, 2)
|
||||
|
||||
votes == 5 // true
|
||||
*voteCounter == 5 // true
|
||||
```
|
||||
|
||||
## 4. Create the election results
|
||||
|
||||
With all the votes now counted, it's time to prepare the result announcement to the whole school.
|
||||
For this, you notice that having only counters for the votes is insufficient.
|
||||
There needs to be a way to associate the number of votes with a particular candidate.
|
||||
|
||||
Create a function `NewElectionResult` that receives the name of a candidate and their number of votes and
|
||||
returns a new election result.
|
||||
|
||||
```go
|
||||
var result *ElectionResult
|
||||
result = NewElectionResult("Peter", 3)
|
||||
|
||||
result.Name == "Peter" // true
|
||||
result.Votes == 3 // true
|
||||
```
|
||||
|
||||
The election result struct is already created for you and it's defined as:
|
||||
|
||||
```go
|
||||
type ElectionResult struct {
|
||||
// Name of the candidate
|
||||
Name string
|
||||
// Votes of votes the candidate had
|
||||
Votes int
|
||||
}
|
||||
```
|
||||
|
||||
## 5. Announce the results
|
||||
|
||||
It's time to announce the new president to the school!
|
||||
The president will be announced in the little digital message boards that the school has.
|
||||
The message should show the name of the new president and the votes they had, in the following format: `<candidate_name> (<votes>)`. This is an example of such message: `"Peter (51)"`.
|
||||
|
||||
Create a function `DisplayResult` that will receive an `*ElectionResult` as an argument and will return a string with the message to display.
|
||||
|
||||
|
||||
```go
|
||||
var result *ElectionResult
|
||||
result = &ElectionResult{
|
||||
Name: "John",
|
||||
Votes: 32,
|
||||
}
|
||||
|
||||
DisplayResult(result)
|
||||
// => John (32)
|
||||
```
|
||||
|
||||
## 6. Vote recounting
|
||||
|
||||
To make sure the final results were accurate, the votes were recounted. In the recount, it was found that the number votes for some of the candidates was off by one.
|
||||
|
||||
Create a function `DecrementVotesOfCandidate` that receives the final results and the name of a candidate for which you should decrement its vote count. The final results are given in the form of a `map[string]int`, where the keys are the names of the candidates and the values are its total votes.
|
||||
|
||||
```go
|
||||
var finalResults = map[string]int{
|
||||
"Mary": 10,
|
||||
"John": 51,
|
||||
}
|
||||
|
||||
DecrementVotesOfCandidate(finalResults, "Mary")
|
||||
|
||||
finalResults["Mary"]
|
||||
// => 9
|
||||
```
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @andrerfcsantos
|
32
election-day/election_day.go
Normal file
32
election-day/election_day.go
Normal file
|
@ -0,0 +1,32 @@
|
|||
package electionday
|
||||
|
||||
// NewVoteCounter returns a new vote counter with
|
||||
// a given number of initial votes.
|
||||
func NewVoteCounter(initialVotes int) *int {
|
||||
panic("Please implement the NewVoteCounter() function")
|
||||
}
|
||||
|
||||
// VoteCount extracts the number of votes from a counter.
|
||||
func VoteCount(counter *int) int {
|
||||
panic("Please implement the VoteCount() function")
|
||||
}
|
||||
|
||||
// IncrementVoteCount increments the value in a vote counter.
|
||||
func IncrementVoteCount(counter *int, increment int) {
|
||||
panic("Please implement the IncrementVoteCount() function")
|
||||
}
|
||||
|
||||
// NewElectionResult creates a new election result.
|
||||
func NewElectionResult(candidateName string, votes int) *ElectionResult {
|
||||
panic("Please implement the NewElectionResult() function")
|
||||
}
|
||||
|
||||
// DisplayResult creates a message with the result to be displayed.
|
||||
func DisplayResult(result *ElectionResult) string {
|
||||
panic("Please implement the DisplayResult() function")
|
||||
}
|
||||
|
||||
// DecrementVotesOfCandidate decrements by one the vote count of a candidate in a map.
|
||||
func DecrementVotesOfCandidate(results map[string]int, candidate string) {
|
||||
panic("Please implement the DecrementVotesOfCandidate() function")
|
||||
}
|
197
election-day/election_day_test.go
Normal file
197
election-day/election_day_test.go
Normal file
|
@ -0,0 +1,197 @@
|
|||
package electionday
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewVoteCounter(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
votes int
|
||||
}{
|
||||
{
|
||||
name: "Simple vote counter with 2 votes",
|
||||
votes: 2,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := NewVoteCounter(tt.votes)
|
||||
if got == nil {
|
||||
t.Errorf("NewVoteCounter(%d) = %s, &%d", tt.votes, intPtrRepresentation(got), tt.votes)
|
||||
}
|
||||
if got != nil && *got != tt.votes {
|
||||
t.Errorf("NewVoteCounter(%d) = %s, &%d", tt.votes, intPtrRepresentation(got), tt.votes)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestVoteCount(t *testing.T) {
|
||||
twoVotes := 2
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
counter *int
|
||||
expected int
|
||||
}{
|
||||
{
|
||||
name: "Call to VoteCount with a nil argument",
|
||||
counter: nil,
|
||||
expected: 0,
|
||||
},
|
||||
{
|
||||
name: "Call to VoteCount with a pointer to an int with a value of 2",
|
||||
counter: &twoVotes,
|
||||
expected: 2,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := VoteCount(tt.counter); got != tt.expected {
|
||||
t.Fatalf("VoteCount(%v) = %d, want %d", intPtrRepresentation(tt.counter), got, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIncrementVoteCount(t *testing.T) {
|
||||
twoVotes := 2
|
||||
fiveVotes := 5
|
||||
noVotes := 0
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
counter *int
|
||||
increment int
|
||||
expected int
|
||||
}{
|
||||
{
|
||||
name: "Call to IncrementVoteCount with a pointer to an int with a value of 0 and increment of 1",
|
||||
counter: &noVotes,
|
||||
increment: 1,
|
||||
expected: 1,
|
||||
},
|
||||
{
|
||||
name: "Call to IncrementVoteCount with a pointer to an int with a value of 2 and increment of 2",
|
||||
counter: &twoVotes,
|
||||
increment: 2,
|
||||
expected: 4,
|
||||
},
|
||||
{
|
||||
name: "Call to IncrementVoteCount with a pointer to an int with a value of 5 and increment of 7",
|
||||
counter: &fiveVotes,
|
||||
increment: 7,
|
||||
expected: 12,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
before := intPtrRepresentation(tt.counter)
|
||||
IncrementVoteCount(tt.counter, tt.increment)
|
||||
after := intPtrRepresentation(tt.counter)
|
||||
|
||||
if tt.counter == nil {
|
||||
t.Errorf("counter before: %s | counter after: %v | wanted: &%d", before, after, tt.expected)
|
||||
}
|
||||
|
||||
if tt.counter != nil && *tt.counter != tt.expected {
|
||||
t.Errorf("counter before: %s | counter after: %v | wanted: &%d", before, after, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewElectionResult(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
candidateName string
|
||||
votes int
|
||||
wanted ElectionResult
|
||||
}{
|
||||
{
|
||||
name: "Call to NewElectionResult for Peter with 2 votes",
|
||||
candidateName: "Peter",
|
||||
votes: 2,
|
||||
wanted: ElectionResult{
|
||||
Name: "Peter",
|
||||
Votes: 2,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := NewElectionResult(tt.candidateName, tt.votes)
|
||||
|
||||
if result == nil || result.Name != tt.wanted.Name || result.Votes != tt.wanted.Votes {
|
||||
t.Errorf("NewElectionResult(\"%s\", %d) = %#v, wanted %#v",
|
||||
tt.candidateName, tt.votes, result, tt.wanted)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDisplayResult(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
result *ElectionResult
|
||||
wanted string
|
||||
}{
|
||||
{
|
||||
name: "Call to DisplayResult for John with 5 votes",
|
||||
result: &ElectionResult{
|
||||
Name: "John",
|
||||
Votes: 5,
|
||||
},
|
||||
wanted: "John (5)",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if result := DisplayResult(tt.result); result != tt.wanted {
|
||||
t.Errorf("DisplayResult(%#v) = %s, wanted %s", *tt.result, result, tt.wanted)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecrementVotesOfCandidate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
candidate string
|
||||
results map[string]int
|
||||
wanted int
|
||||
}{
|
||||
{
|
||||
name: "Call to DecrementVotesOfCandidate for John with 3 votes",
|
||||
candidate: "John",
|
||||
results: map[string]int{
|
||||
"John": 3,
|
||||
},
|
||||
wanted: 2,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
DecrementVotesOfCandidate(tt.results, tt.candidate)
|
||||
if votes, ok := tt.results[tt.candidate]; !ok || votes != tt.wanted {
|
||||
t.Errorf("DecrementVotesOfCandidate(%v) | wanted %d, got %d",
|
||||
tt.results, tt.wanted, votes)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func intPtrRepresentation(p *int) string {
|
||||
if p == nil {
|
||||
return "nil"
|
||||
}
|
||||
return "&" + strconv.Itoa(*p)
|
||||
}
|
9
election-day/election_result.go
Normal file
9
election-day/election_result.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
package electionday
|
||||
|
||||
// ElectionResult represents an election result.
|
||||
type ElectionResult struct {
|
||||
// Name is the name of the candidate.
|
||||
Name string
|
||||
// Votes is the total number of votes the candidate had.
|
||||
Votes int
|
||||
}
|
3
election-day/go.mod
Normal file
3
election-day/go.mod
Normal file
|
@ -0,0 +1,3 @@
|
|||
module electionday
|
||||
|
||||
go 1.18
|
Loading…
Add table
Add a link
Reference in a new issue