Example of how you can use pointers to create a counter method of an int type
The below won't work as on a copy of x is passed to the method.
type Number int
func (n Number) Double() {
n *= 2
}
func main() {
x := Number(4)
fmt.Println(x)
x.Double()
fmt.Println(x)
}
// result
4
4
You can convert the method to receive pointers without having to change in code in main
type Number int
func (n *Number) Double() {
*n *= 2
}
// Result
4
8
If a struct is being used, then it is dereferenced automatically by Go
type Date struct {
year int
}
// Setters
func (d *Date) SetYear(year int) {
d.year = year
// (*d).year = year could do this, but no need as structs are automatically dereferenced
}
Read in a text file line by line.
func main() {
var lines []string
// Read in text file that contains names. One name per line
// Each time a name is read, it is counted as a vote for that person.
// At the end, the votes are totalled and printed in order from highest to lowest
file, err := os.Open("votes.txt")
if err != nil {
log.Fatal(err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
lines = append(lines, line)
}
if scanner.Err() != nil {
log.Fatal(err)
}
// Use a map to total the votes
unsortedVotes := make(map[string]int)
for _, v := range lines {
unsortedVotes[v]++
}
// Now votes are counted, diplay them.
//copy them to a slice which can be sorted
type votes struct {
name string
votes int
}
// Build a slice from the map, so that we can sort the slice
sortedVotes := make([]votes, 0, len(unsortedVotes))
for n, v := range unsortedVotes {
sortedVotes = append(sortedVotes, votes{n, v})
}
// Sort in descending order by votes (highest first)
sort.Slice(sortedVotes, func(i, j int) bool {
return sortedVotes[i].votes > sortedVotes[j].votes
})
for _, result := range sortedVotes {
fmt.Printf("%v\t%v\n", result.name, result.votes)
}
}
func main() {
args := os.Args
if len(args) == 1 {
log.Fatal("Must supply a directory")
}
dirname := args[1]
err := scanDirectory(dirname)
if err != nil {
log.Fatal(err)
}
}
func scanDirectory(path string) error {
files, err := os.ReadDir(path)
if err != nil {
log.Fatal("Fatal, os.Readir: ", err)
}
for _, file := range files {
filePath := filepath.Join(path, file.Name())
if file.IsDir() {
err := scanDirectory(filePath)
if err != nil {
return err
}
} else {
fmt.Println(filePath)
}
}
return nil
}
A simple example of a basic interface
There are 3 types defined with various methods.
These three types have two methods in common. Play and Stop
If we want to write a function that incorporates Play and Stop, we need to write it three times as each function can only deal with a single type.
So, we have functions, playList, playListRec, PlayListStereo that are all identical except for the type that they can use.
First an example without Interfaces, then how we can improve it with.
func main() {
cocko := TapePlayer{"enegizer"}
cuntos := TapeRecorder{1}
dickwad := Stereo{}
mymix := []string{"gagf", "go and get fucked", "you cuntos"}
playList(cocko, mymix)
playListRec(cuntos, mymix)
playListStereo(dickwad, mymix)
}
// Tape Player
type TapePlayer struct {
Batteries string
}
func (t TapePlayer) Play(song string) {
fmt.Println("Playing on my player", song)
}
func (t TapePlayer) Stop() {
fmt.Println("Stopped!")
}
type TapeRecorder struct {
Microphones int
}
// Tape Recorder
func (t TapeRecorder) Play(song string) {
fmt.Println("Playing on my recorder", song)
}
func (t TapeRecorder) Stop() {
fmt.Println("Stopped")
}
func (t TapeRecorder) Record() {
fmt.Println("Recording now...")
}
// Stereo
type Stereo struct{}
func (s Stereo) Play(song string) {
fmt.Println("Playing on the stereo", song)
}
func (s Stereo) Stop() {
fmt.Println("Stopped")
}
func (s Stereo) PlayLoud(song string) {
fmt.Println("PLAYING ON THE STEREO...LOUD", song)
}
// Functions
func playList(device TapePlayer, songs []string) {
for _, song := range songs {
device.Play(song)
}
device.Stop()
}
func playListRec(device TapeRecorder, songs []string) {
for _, song := range songs {
device.Play(song)
}
device.Stop()
}
func playListStereo(device Stereo, songs []string) {
for _, song := range songs {
device.Play(song)
}
device.Stop()
}
By creating an Interface for the methods that all three types share, we can then create a single function that can operate on that interface.
Change to the above code.
The interface definition of Player (lines 12-15) was added.
The three functions of playlist, playlistrec, playliststereo where replaced with a single function play which is identical, except that that type that it accepts as an argument is now of interface of type Player
Lines 7-9 in func main were changed to all call the same func play
While in this example, it's not a huge reduction in code size, the main thing is that there is no duplicate play.... type function.
There is just one fucntion works regardless of type.
func main() {
cocko := TapePlayer{"enegizer"}
cuntos := TapeRecorder{1}
dickwad := Stereo{}
mymix := []string{"gagf", "go and get fucked", "you cuntos"}
play(cocko, mymix)
play(cuntos, mymix)
play(dickwad, mymix)
}
type Player interface {
Play(string)
Stop()
}
// Tape Player
type TapePlayer struct {
Batteries string
}
func (t TapePlayer) Play(song string) {
fmt.Println("Playing on my player", song)
}
func (t TapePlayer) Stop() {
fmt.Println("Stopped!")
}
type TapeRecorder struct {
Microphones int
}
// Tape Recorder
func (t TapeRecorder) Play(song string) {
fmt.Println("Playing on my recorder", song)
}
func (t TapeRecorder) Stop() {
fmt.Println("Stopped")
}
func (t TapeRecorder) Record() {
fmt.Println("Recording now...")
}
// Stereo
type Stereo struct{}
func (s Stereo) Play(song string) {
fmt.Println("Playing on the stereo", song)
}
func (s Stereo) Stop() {
fmt.Println("Stopped")
}
func (s Stereo) PlayLoud(song string) {
fmt.Println("PLAYING ON THE STEREO...LOUD", song)
}
// Functions
func play(device Player, songs []string) {
for _, song := range songs {
device.Play(song)
}
device.Stop()
}
Using a similar example to above, we now have a neww TryOut() that uses a Type Assertion so that if a type player is passed to it that happens to have an underlying type of TapeRecorder, it can still access its Record method.
The code will compile even if you pass a player that is of type TapePlayer, so you need to do error checking on the assertion to prevent a runtime panic if you try to Record() on a TapePlayer
main.go
package main
import (
"fmt"
"log"
"testes/gadget"
)
type Player interface {
Play(string)
Stop()
}
// Functions
func PlayList(device Player, songs []string) {
for _, song := range songs {
device.Play(song)
}
device.Stop()
}
func TryOut(player Player) {
player.Play("Test Track")
player.Stop()
if recorder, ok := player.(gadget.TapeRecorder); ok {
recorder.Record()
} else {
log.Println("Player was not a Tape Recorder!")
}
}
func main() {
mymix := []string{"gagf", "go and get fucked", "you cuntos"}
var cocko Player = gadget.TapePlayer{}
PlayList(cocko, mymix)
fmt.Println("-=-=-=-=-")
TryOut(gadget.TapePlayer{})
TryOut(gadget.TapeRecorder{})
}
tape.go
package gadget
import "fmt"
// Tape Player
type TapePlayer struct {
Batteries string
}
func (t TapePlayer) Play(song string) {
fmt.Println("Playing on my player", song)
}
func (t TapePlayer) Stop() {
fmt.Println("Stopped!")
}
// Tape Recorder
type TapeRecorder struct {
Microphones int
}
func (t TapeRecorder) Play(song string) {
fmt.Println("Playing on my recorder", song)
}
func (t TapeRecorder) Stop() {
fmt.Println("Stopped")
}
func (t TapeRecorder) Record() {
fmt.Println("Recording now...")
}
Simple example of Go Routines that go and retrieve a number of web pages and report on their size.
type Page struct {
URL string
Size int
}
func main() {
urlchan := make(chan Page)
urls := []string{
"https://www.google.com",
"https://go.dev",
"https://www.github.com",
}
for _, url := range urls {
go responseSize(urlchan, url)
}
for range urls {
page := <-urlchan
fmt.Printf("%s: %d KB\n", page.URL, page.Size)
}
}
func responseSize(c chan Page, url string) {
resp, err := http.Get(url)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
c <- Page{URL: url, Size: len(body) / 1024}
}
func TestWithOneElement(t *testing.T) {
list := []string{"cocko"}
want := "cocko"
got := JoinWithCommas(list)
if got != want {
t.Error(errorString(list, got, want))
}
}
func TestWithTwoElements(t *testing.T) {
list := []string{"cocko", "cunto"}
want := "cocko and cunto"
got := JoinWithCommas(list)
if got != want {
t.Error(errorString(list, got, want))
}
}
func TestWithThreeElements(t *testing.T) {
list := []string{"cocko", "cunto", "fuckerwit"}
want := "cocko, cunto and fuckerwit"
got := JoinWithCommas(list)
if got != want {
t.Error(errorString(list, got, want))
}
}
func errorString(list []string, got, want string) string {
return fmt.Sprintf("\nPassed: %#v\nGot: %s\nWant: %s", list, got, want)
}
The above can be substantially reduced using Table driven tests
type testData struct {
list []string
want string
}
func TestJointWithCommas(t *testing.T) {
tests := []testData{
{list: []string{"cocko"}, want: "cocko"},
{list: []string{"cocko", "cunto"}, want: "cocko and cunto"},
{list: []string{"cocko", "cunto", "fuckerwit"}, want: "cocko, cunto and fuckerwit"},
}
for _, test := range tests {
got := JoinWithCommas(test.list)
if got != test.want {
t.Error(errorString(test.list, got, test.want))
}
}
}
func errorString(list []string, got, want string) string {
return fmt.Sprintf("\nPassed: %#v\nGot: %s\nWant: %s", list, got, want)
}
Examples of creating a 2d matrix.
It takes the number of rows and columns, and returns a maxtrix of each row and columns multiplies against each other.
func createMatrix(rows, cols int) [][]int {
matrix := make([][]int, rows)
for r := 0; r < rows; r++ {
matrix[r] = make([]int,cols)
for c := 0; c < cols; c++ {
matrix[r][c] = r * c
}
}
return matrix
}
// 3 rows and 3 columns
[0 0 0 0]
[0 1 2 3]
[0 2 4 6]
func concurrentFib(n int) []int {
ch := make(chan int)
fibs := make([]int, 0,n)
go fibonacci(n, ch)
for fib := range ch {
fibs = append (fibs, fib)
}
return fibs
}
func fibonacci(n int, ch chan int) {
x, y := 0, 1
for i := 0; i < n; i++ {
ch <- x
x, y = y, x+y
}
close(ch)
}