Go Tutorial Part 4 (Interfaces, Routines, Channels, Concurrency in Go)

GO - Part 4

Credit goes to - Master the fundamentals and advanced features of the Go Programming Language (Golang), on Udemy by Stephen Grider.
I have tried some snippets on my own and some are referring to the snippets developed while going through the course.

Code Repo: https://github.com/namitsharma99/goLangTutorials




Interfaces
- - - - - - - - - 

Need:
Whenever there is a logic change in a function, it has to be re-written.


For example, maintaining 2 chatboxes :
A.
1. type hindiBot struct

2. func (hindiBot) getGreeting() string // return "namaste"

3. func printGreeting(hb hindiBot) // similar code - fmt.Println(hb.getGreeting())

B.
1. type englishBot struct

2. func (englishBot) getGreeting() string // return "hello"

3. func printGreeting(eb englishBot) // similar code - fmt.Println(eb.getGreeting())


Code --

package main

import "fmt"

type hindiBot struct {}
type englishBot struct {}

func main()  {

}

//func (hb hindiBot) getGreeting() string {
func (hindiBot) getGreeting() string {  // since no hb value is read
  return "namaste!!"
}

// func (eb englishBot) getGreeting() string {
func (eb englishBot) getGreeting() string { // since no eb value is read
  return "hello!!"
}

func printGreeting(hb hindiBot) {
  fmt.Println(hb.getGreeting())
}

func printGreeting(eb englishBot) {
  fmt.Println(eb.getGreeting())
}

ERROR::

$ go build main.go 
# command-line-arguments
./main.go:26:6: printGreeting redeclared in this block
previous declaration at ./main.go:22:23


FIX 1:

package main

import "fmt"

type hindiBot struct {}
type englishBot struct {}

func main()  {
  hb := hindiBot{}
  eb := englishBot{} // gives error

  printGreeting(hb)
  printGreeting(eb)

}

//func (hb hindiBot) getGreeting() string {
func (hindiBot) getGreeting() string {  // since no hb value is read
  return "namaste!!"
}

// func (eb englishBot) getGreeting() string {
func (eb englishBot) getGreeting() string { // since no eb value is read
  return "hello!!"
}

// ERROR
/* func printGreeting(hb hindiBot) {
  fmt.Println(hb.getGreeting())
}

func printGreeting(eb englishBot) {
  fmt.Println(eb.getGreeting())
} */
// printGreeting redeclared in this block

// using single print method
func printGreeting(hb hindiBot) {
  fmt.Println(hb.getGreeting())
}

ERROR::
$ go build main.go 
# command-line-arguments
./main.go:13:16: cannot use eb (type englishBot) as type hindiBot in argument to printGreeting


FIX 2: 

-- using interfaces

package main

import "fmt"

// the interface fix
type bot interface {
  getGreeting() string
}


type hindiBot struct {}
type englishBot struct {}

func main()  {
  hb := hindiBot{}
  eb := englishBot{}

  printGreeting(hb)
  printGreeting(eb)

}

//func (hb hindiBot) getGreeting() string {
func (hindiBot) getGreeting() string {  // since no hb value is read
  return "namaste!!"
}

// func (eb englishBot) getGreeting() string {
func (eb englishBot) getGreeting() string { // since no eb value is read
  return "hello!!"
}

// ERROR
/* func printGreeting(hb hindiBot) {
  fmt.Println(hb.getGreeting())
}

func printGreeting(eb englishBot) {
  fmt.Println(eb.getGreeting())
} */
// printGreeting redeclared in this block

func printGreeting(b bot) {
  fmt.Println(b.getGreeting())
}


O/P
- - -

$ ./main 
namaste!!
hello!!


WORKING:

Interface identifies based on the func associated.

type bot interface {
  getGreeting() string
}

This declares that any type, having getGreeting() func and returning the string, automatically becomes a member of this interface

Since hb and eb above, are now the members, they can call the printGreeting() func easily using the bot interface

Note:
------
Argument type can also be put in the clause along with the type check as follows :

type bot interface {
  getGreeting(string, int) string
  // and many other functions
}


- interfaces are not generic types
- interfaces are implicit, matched with signature clauses only
- interfaces just help in maintaining types, no further validation



net/ http 
- - - - - - - - 

package main

import "fmt"
import "net/http"
import "os"

func main() {
  resp, err := http.Get("http://www.google.com")
  if nil != err {
    fmt.Println("Error: ", err)
    os.Exit(1)
  }
  fmt.Println("response: ",resp)
}

O/P
- - -

$ ./main 
response:  &{200 OK 200 HTTP/1.1 1 1 map[Cache-Control:[private, max-age=0] Content-Type:[text/html; charset=ISO-8859-1] Date:[Wed, 20 May 2020 21:41:29 GMT] Expires:[-1] P3p:[CP="This is not a P3P policy! See g.co/p3phelp for more info."] Server:[gws] Set-Cookie:[1P_JAR=2020-05-20-21; expires=Fri, 19-Jun-2020 21:41:29 GMT; path=/; domain=.google.com; Secure NID=204=V-0Lr33CGR1OSs2-eLQShrsFqM23NpD66sBXPp4lJky4VKQ1LSL6Uoz6SpRpUuDB8HToPfN6YFbT_cgGjnO595t07MExURpcb5UeC_juxvM0dMZrQduvG8FUJ5gsIzCNGCWumrxNWyrEhntEvhUBw9rfjkE9w6u7zzoq3_05ltI; expires=Thu, 19-Nov-2020 21:41:29 GMT; path=/; domain=.google.com; HttpOnly] X-Frame-Options:[SAMEORIGIN] X-Xss-Protection:[0]] 0xc00011e0e0 -1 [] false true map[] 0xc0001ee000 <nil>}



Some more info on Interfaces
- - - - - - - - - - - - - - - - - - - - - - - - -

Reader Interface - provides common implementation to read any source as byte slice - []byte, which can work with anything further

package main

import "fmt"
import "net/http"
import "os"

func main() {
  resp, err := http.Get("http://www.google.com")
  if nil != err {
    fmt.Println("Error: ", err)
    os.Exit(1)
  }
  // fmt.Println("response: ",resp)
  bs := make([]byte, 99999)
  resp.Body.Read(bs)
  fmt.Println("byte slice: ", bs)
  fmt.Println("byte slice as string: ", string(bs))

}


O/P
---
< Many byte characters and huge response >

Another option to print response -
...
io.Copy(os.Stdout, resp.Body)
...


Writer Interface - opposite of Reader interface, takes byte slice and converts into desired format

io.Copy(os.Stdout, resp.Body) --> the params are implementation of Writer interface and Reader interface respectively

Copy() internally uses CopyBuffer()


package main

import "fmt"
import "net/http"
import "os"
import "io"

type logWriter struct {}

func main() {
  resp, err := http.Get("http://www.google.com")
  if nil != err {
    fmt.Println("Error: ", err)
    os.Exit(1)
  }
  // fmt.Println("response: ",resp)

  // bs := make([]byte, 99999)
  // resp.Body.Read(bs)
  // fmt.Println("byte slice: ", bs)
  // fmt.Println("byte slice as string: ", string(bs))

  // io.Copy(os.Stdout, resp.Body)

  lw := logWriter{}
  io.Copy(lw, resp.Body)

}

func (logWriter) Write(bs []byte) (int, error) {
  // return 1, nil
  fmt.Println(string(bs))
  fmt.Println("length ---- ", len(bs))
  return len(bs), nil
}

o/p
- - - 

< response body >



Channels and Go Routines
- - - - - - - - - - - - - - - - - - - - - - -

Example Code:

main.go
- - - - 
package main

import "fmt"
import "net/http"

func main() {
  links := []string {
    "http://www.google.com",
    "http://www.golang.org",
    "http://www.stackoverflow.com",
    "http://www.amazon.com",
  }

  for _, link := range links {
    checkLink(link)
  }

}

func checkLink(link string) {
  _, err := http.Get(link)
  if nil != err {
    fmt.Println(link, " is down!!")
    return
  }
  fmt.Println(link, " is up!!")
}

O/P
- - -

$ ./main 
http://www.google.com  is up!!
http://www.golang.org  is up!!
http://www.stackoverflow.com  is up!!
http://www.amazon.com  is up!!



Note: As clear from the response, this is sequential and involves waiting. Now, need to see how to make the calls in parallel - Go Routines.

Go Routine lets the Main Routines knows, that there is a blocking function and Main should continue.

Syntax: just need to add go prefix against the function calls
go checkLink(link)

Go Scheduler takes care of the monitoring. Be default, it uses only one CPU core, but it can be customized to distribute the load.

Concurrency/ Parallelism can be achieved once multiple cores are involved.

Main routine doesn't give any priority to the child routines hence they are not executed. Therefore, need to use Channels.
It acts as a mediator to communicate between the Main routine and the child routines.
Channels are strictly defined per types, string channel can't support the int type.


How to send data with Channels

channel <- 9         : send the value 9 into channel
n <- channel         : waiting for channel to get the value and assigning it further to the variable n

fmt.Println(<- channel) : waiting for channel to get the value and hence printing it afterwards


Code:

main.go
- - - - - -
package main

import "fmt"
import "net/http"

func main() {
  links := []string {
    "http://www.google.com",
    "http://www.golang.org",
    "http://www.stackoverflow.com",
    "http://www.amazon.com",
  }

  c := make(chan string) // need to pass this channel in function call

  for _, link := range links {
    go checkLink(link, c)
  }
  fmt.Println(<- c)
}

func checkLink(link string, c chan string) {
  _, err := http.Get(link)
  if nil != err {
    fmt.Println(link, " is down!!")
    c <- "down !!"
    return
  }
  fmt.Println(link, " is up!!")
  c <- "up!!"
}


O/P
- - -
$ ./main 
http://www.google.com  is up!!
up!!


One issue - only one url is checked...

Because, Main Routine proceeds as soon as one of the child routines comes back.
Message receiving is also a blocking code.

Making the channel receiver again in Main routine -

...
  fmt.Println(<- c)
  fmt.Println(<- c)
...
O/P
- - -
$ ./main 
http://www.google.com  is up!!
up!!
http://www.stackoverflow.com  is up!!
up!!

All results can be seen if same number of receivers are created, but hangs if any additional is there !!

Quick hack -
...
  for i :=0; i< len(links); i++ {
    fmt.Println(<- c)
  }
...



How to repeat the Routines?
- - - - - - - - - - - - - - - - - - - - - - - 

Passing links instead of values, and handling at the receiver ends

package main

import "fmt"
import "net/http"

func main() {
  links := []string {
    "http://www.google.com",
    "http://www.golang.org",
    "http://www.stackoverflow.com",
    "http://www.amazon.com",
  }

  c := make(chan string) // need to pass this channel in function call

  for _, link := range links {
    go checkLink(link, c)
  }
  // fmt.Println(<- c)
  // fmt.Println(<- c)

  // for i :=0; i< len(links); i++ {
  //   fmt.Println(<- c)
  // }

  for {
    go checkLink(<- c, c)
  }

}

func checkLink(link string, c chan string) {
  _, err := http.Get(link)
  if nil != err {
    fmt.Println(link, " is down!!")
    // c <- "down !!"
    c <- link
    return
  }
  fmt.Println(link, " is up!!")
  // c <- "up!!"
  c <- link
}


O/P
- - - 
< Going on and on... forever>



Alternatively, making it more readable --
...
  for l := range c {
    go checkLink(l, c)
  }
...



How to add a pause in between the checks?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

Use Time package, sleep function
Avoid adding pauses in the main routines, it's a bad way - blocking and queuing 

Use function literals, i.e. unnamed functions

func main() {
  links := []string {
    "http://www.google.com",
    "http://www.golang.org",
    "http://www.stackoverflow.com",
    "http://www.amazon.com",
  }

  c := make(chan string) // need to pass this channel in function call

  for _, link := range links {
    go checkLink(link, c)
  }

  // Alternative syntax
  for l := range c {
    // time.Sleep(5*time.Second) // 5 seconds, bad way - blocking and queuing
    // go checkLink(l, c)
    go func() {  // function literals, like unnamed and anonymous functions
        time.Sleep(5*time.Second)
        go checkLink(l, c)
      }  ()
  }

}

Issue: l is not a trustworthy variable to monitor for the Main routine, as it's value might have changed.
Output can be like first run happening fine for all URLs but after that stuck with one url's copy. (Go being pass by copy)

Fix, pass l from the range -
...
  for l := range c {
    go func(link string) {  
        time.Sleep(5*time.Second)
        go checkLink(link, c)
      }  (l)
  }
...

O/P AS EXPECTED -
- - - - - - - - - 

$ ./main 
http://www.google.com  is up!!
http://www.stackoverflow.com  is up!!
http://www.amazon.com  is up!!
http://www.golang.org  is up!!
http://www.google.com  is up!!
http://www.stackoverflow.com  is up!!
http://www.amazon.com  is up!!
http://www.golang.org  is up!!
http://www.google.com  is up!!
http://www.stackoverflow.com  is up!!
http://www.amazon.com  is up!!
^C

Go Tutorial Part 3 (Structs, Pointers and References, Maps, Structs vs Maps in Go)

GO - Part 3

Credit goes to - Master the fundamentals and advanced features of the Go Programming Language (Golang), on Udemy by Stephen Grider.
I have tried some snippets on my own and some are referring to the snippets developed while going through the course.

Code Repo: https://github.com/namitsharma99/goLangTutorials



Structs data structure
- - - - - - - - - - - - - - - - - - 
- collection of properties related together

2 steps to define structs:
1. provide GO, the fields struct should have
2. create the object using the above


main.go
- - - - 

package main

import "fmt"

type person struct {
  firstName string
  lastName string
}

func main() {
  buddy := person {"code", "buddy"} // not a good practice as it depends on positions
  namit := person {firstName: "Namit", lastName: "Sharma"}

  fmt.Println(buddy)
  fmt.Println(namit)
}

O/P
- - - 

$ go build
$ ls
  main.go structs
$ ./structs 
  {code buddy}
  {Namit Sharma}

For uninitialized structs, if we want to see the struct signature - 

...
  var human person
  fmt.Println(human)
  fmt.Printf("%+v", human)
...

o/p
- - -
{ }
{firstName: lastName:}


...
  human.firstName = "ABC"
  human.lastName = "DEF"

  fmt.Println(human)
  fmt.Printf("%+v", human)
...

o/p
- - -
{ABC DEF}
{firstName:ABC lastName:DEF}




Nested Structs
- - - - - - - - - - - - -

package main

import "fmt"

type contactInfo struct {
  email string
  zipCode int
}

type person struct {
  firstName string
  lastName string
  contact contactInfo
}

func main() {
 
  newPerson := person {
    firstName:"Namit",
    lastName:"Sharma",
    contact: contactInfo {
      email: "thecodebuddy.blogspot.in",
      zipCode: 60001,
    },
  }
  // multiline declarations should have commas at the end
  
  fmt.Println("%+v", newPerson)

}

o/p
- - -

$ ./main 
%+v {Namit Sharma {thecodebuddy.blogspot.in 60001}}


Note - if the name of the variable is same as the type name, just use type - 

package main
import "fmt"

type contactInfo struct {
  email string
  zipCode int
}

type person struct {
  firstName string
  lastName string
  contactInfo
}

func main() {

newPerson := person {
    firstName:"Namit",
    lastName:"Sharma",
    contactInfo: contactInfo {
      email: "thecodebuddy.blogspot.in",
      zipCode: 60001,
    },
  }
  // multiline declarations should have commas at the end

  fmt.Printf("%+v", newPerson)
  fmt.Println("")

}

o/p
- - -

$ ./main 
{firstName:Namit lastName:Sharma contactInfo:{email:thecodebuddy.blogspot.in zipCode:60001}}



 
Structs and Receiver Functions
- - - - - - - - - - - - - - - - - - - - - - - - - - 

func main () {
...
  fmt.Printf("%+v", newPerson)
  fmt.Println("")
  newPerson.print()
...
}

o/p
- - - 

{firstName:Namit lastName:Sharma contactInfo:{email:thecodebuddy.blogspot.in zipCode:60001}}
{firstName:Namit lastName:Sharma contactInfo:{email:thecodebuddy.blogspot.in zipCode:60001}}




Updating values inside struct
- - - - - - - - - - - - - - - - - - - - - - - - -

func (p person) updateName(newFirstName string) {
  p.firstName = newFirstName
}


This fails as there is another memory allocated to the changes made, original reference has no impact (pass by copy), therefore use pointers

...
  fmt.Println("updating name::")
  newPersonPointer := &newPerson  // Optional to define a pointer to person, func can accept the person object as well
  newPersonPointer.updateName("XYZ")
  newPersonPointer.print()
...

func (ptr *person) updateName(newFirstName string) {
  (*ptr).firstName = newFirstName
}


o/p
- - -
updating name::
{firstName:XYZ lastName:Sharma contactInfo:{email:thecodebuddy.blogspot.in zipCode:60001}}


Explanation -

& operator : memory address of variable
* operator : value at the address

Rules:
1. To turn address into value, use *address
2. To turn value into address, use &value



Note::

Pointers are not required while working with Slices
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Slices internally use Arrays

Slice = length | capacity | ptr to head of array

Now while making any change to slices, a copy of slice is created, but still it is pointing to the actual address
Hence it works without any fail.
Therefore, a slice is called as of 'Reference Type'.

Value Types: int, float, string, bool, structs  (use pointers while working with these)
Reference Types: slices, maps, channels, pointers, functions




Maps
- - - - -

All keys of same type
All values of same type


main.go
-------
package main

import "fmt"

func main() {
  colors := map[string] string {
    "red" : "#ff0000",
    "green" : "#433243",
  }
  fmt.Println(colors)
}

O/P
- - -
$ ./main 
map[green:#433243 red:#ff0000]


Also, map be declared as - 
1. var colors map[string] string
2. colors := make(map[string]string)

func main() {
  // colors := map[string] string {
  //   "red" : "#ff0000",
  //   "green" : "#433243",
  // }

  // var colors map[string] string
  colors := make(map[string]string)

  colors["white"] = "#ffffff"
  colors["grey"] = "#222222"
  fmt.Println(colors)

  delete(colors, "white")  // to delete using a key
  fmt.Println(colors)
}

O/P
- - -
$ ./main 
map[grey:#222222 white:#ffffff]
map[grey:#222222]



Map Iteration
- - - - - - - - - - - -

func printMap (c map[string]string ) {
  for color, hex := range c {
    fmt.Println("Hex code for ", color, "is ", hex)
  }
}

main.go
...
printMap(colors)
...

o/p
- - -
$ ./main 
map[grey:#222222 white:#ffffff]
Hex code for  white is  #ffffff
Hex code for  grey is  #222222



Map vs Structs
- - - - - - - - - - - - 

Map - 
  • same typed keys
  • same typed values
  • keys are indexed and iterable
  • used to represent collection of related properties
  • no need to have all keys available at compile time
  • reference type

Struct -
  • diff types of values
  • keys are not indexed
  • can be used to represent collection of different properties
  • no need to know all the different fields at compile time
  • value type



Go Tutorial Part 2 (Slicing, File Handling I/O, Error Handling, Test Cases)

GO - Part 2

Credit goes to - Master the fundamentals and advanced features of the Go Programming Language (Golang), on Udemy by Stephen Grider.
I have tried some snippets on my own and some are referring to the snippets developed while going through the course.

Code Repo: https://github.com/namitsharma99/goLangTutorials

ioutil - package that supports in-built functions like WriteFile(), this method would require byte slices

Byte Slices - converted string into ascii codes
--------------------------------------------------------
main.go
...
[]byte("Hello World!")
...

o/p-

  temp := "Hello World!"
  fmt.Println(temp)
  fmt.Println([]byte(temp))
  
  Hello World!
[72 101 108 108 111 32 87 111 114 108 100 33]


How to convert a deck (slice of cards) into a string?
-----------------------------------------------------------------
- use type conversion


main.go
...
cards = newDeck()
  fmt.Println(cards.toString())
...

deck.go
...
func (d deck) toString() string{
  // use strings package
  return strings.Join([]string(d),",")
}
...

o/p-

Ace of Spades,Spades of Ace,Spades of Two,Spades of Three,Spades of Four,Spades of Five,Spades of Six,Spades of Seven,Spades of Eight,Spades of Nine,Spades of Ten,Spades of Jack,Spades of Queen,Spades of King,Diamonds of Ace,Diamonds of Two,Diamonds of Three,Diamonds of Four,Diamonds of Five,Diamonds of Six,Diamonds of Seven,Diamonds of Eight,Diamonds of Nine,Diamonds of Ten,Diamonds of Jack,Diamonds of Queen,Diamonds of King,Hearts of Ace,Hearts of Two,Hearts of Three,Hearts of Four,Hearts of Five,Hearts of Six,Hearts of Seven,Hearts of Eight,Hearts of Nine,Hearts of Ten,Hearts of Jack,Hearts of Queen,Hearts of King,Club of Ace,Club of Two,Club of Three,Club of Four,Club of Five,Club of Six,Club of Seven,Club of Eight,Club of Nine,Club of Ten,Club of Jack,Club of Queen,Club of King
How to Save the data in a file?
--------------------------------------

main.go
...
cards = newDeck()
  cards.saveToFile("helloCards")
...

deck.go
...
func (d deck) saveToFile(fileName string) error {
  return ioutil.WriteFile(fileName, []byte(d.toString()), 0777) // provide permissions
}
...

o/p-
helloCards file is created
Reading a saved file and converting to a deck
-----------------------------------------------------------

using ReadFile() method


main.go
...
cards = newDeckFromFile("helloCards")
  cards.print()
...

deck.go
...
func newDeckFromFile(fileName string) deck {
  bytslc, err := ioutil.ReadFile(fileName) // default error handling
  if err != nil {
    // Option 1 - log error and return a new decks
    // Option 2 - log error and quit
    fmt.Println("Error: ", err)
    os.Exit(1) // imported package os
  }
  s := strings.Split(string(bytslc), ",") // typecasting of byte slice to string, then using split to chunk on the basis of comma
  return deck(s) // using slice of strings to create a deck
}
...

o/p-
0 Ace of Spades
1 Spades of Ace
2 Spades of Two
3 Spades of Three
4 Spades of Four
5 Spades of Five
6 Spades of Six
7 Spades of Seven
8 Spades of Eight
9 Spades of Nine
10 Spades of Ten
11 Spades of Jack
12 Spades of Queen
13 Spades of King
14 Diamonds of Ace
15 Diamonds of Two
16 Diamonds of Three
17 Diamonds of Four
18 Diamonds of Five
19 Diamonds of Six
20 Diamonds of Seven
21 Diamonds of Eight
22 Diamonds of Nine
23 Diamonds of Ten
24 Diamonds of Jack
25 Diamonds of Queen
26 Diamonds of King
27 Hearts of Ace
28 Hearts of Two
29 Hearts of Three
30 Hearts of Four
31 Hearts of Five
32 Hearts of Six
33 Hearts of Seven
34 Hearts of Eight
35 Hearts of Nine
36 Hearts of Ten
37 Hearts of Jack
38 Hearts of Queen
39 Hearts of King
40 Club of Ace
41 Club of Two
42 Club of Three
43 Club of Four
44 Club of Five
45 Club of Six
46 Club of Seven
47 Club of Eight
48 Club of Nine
49 Club of Ten
50 Club of Jack
51 Club of Queen
52 Club of King



How to Shuffle
-------------------

use random numbers, using math package

Fixed routine

main.go
...
  cards = newDeck()
  cards.shuffle()
  cards.print()
...

deck.go
...
func (d deck) shuffle() {
  for i := range d {
    newPosition := rand.Intn(len(d)-1)
    d[i], d[newPosition] = d[newPosition], d[i]
  }
}
...

o/p-

0 Hearts of Three
1 Spades of Ten
2 Hearts of King
3 Hearts of Ace
4 Diamonds of Queen
5 Hearts of Nine
6 Club of Eight
7 Spades of Four
8 Spades of Eight
9 Spades of Three
10 Hearts of Queen
11 Club of Four
12 Diamonds of King
13 Club of King
14 Club of Two
15 Diamonds of Five
16 Club of Ten
17 Club of Six
18 Club of Seven
19 Diamonds of Seven
20 Diamonds of Two
21 Diamonds of Six
22 Spades of Jack
23 Spades of Ace
24 Spades of Queen
25 Diamonds of Three
26 Diamonds of Jack
27 Hearts of Ten
28 Club of Three
29 Club of Queen
30 Spades of Nine
31 Spades of Two
32 Spades of Seven
33 Diamonds of Nine
34 Hearts of Jack
35 Club of Five
36 Spades of King
37 Club of Jack
38 Spades of Five
39 Diamonds of Eight
40 Ace of Spades
41 Club of Ace
42 Hearts of Five
43 Diamonds of Four
44 Spades of Six
45 Hearts of Eight
46 Hearts of Seven
47 Diamonds of Ace
48 Diamonds of Ten
49 Club of Nine
50 Hearts of Six
51 Hearts of Four
52 Hearts of Two


Dynamic routine
- using dynamic seed in the generator

main.go
...
  cards = newDeck()
  cards.shuffle2()
  cards.print()
...

deck.go
...
func (d deck) shuffle2() {
  source := rand.NewSource(time.Now().UnixNano()) // seed
  r := rand.New(source) // generator

  for i := range d {
    newPosition := r.Intn(len(d)-1)
    d[i], d[newPosition] = d[newPosition], d[i]
  }
}
...

o/p-
0 Spades of Five
1 Diamonds of Ten
2 Diamonds of Jack
3 Hearts of Jack
4 Club of Ten
5 Diamonds of Ace
6 Club of Five
7 Spades of King
8 Diamonds of Four
9 Hearts of Seven
10 Spades of Seven
11 Hearts of Eight
12 Spades of Queen
13 Club of Three
14 Club of Two
15 Diamonds of Three
16 Hearts of King
17 Hearts of Four
18 Diamonds of King
19 Hearts of Three
20 Diamonds of Nine
21 Spades of Four
22 Club of Four
23 Hearts of Six
24 Diamonds of Five
25 Spades of Three
26 Club of Seven
27 Ace of Spades
28 Hearts of Two
29 Club of Six
30 Club of Ace
31 Club of Nine
32 Spades of Ten
33 Spades of Nine
34 Spades of Six
35 Diamonds of Queen
36 Spades of Eight
37 Diamonds of Two
38 Diamonds of Seven
39 Diamonds of Eight
40 Spades of Two
41 Spades of Jack
42 Club of King
43 Hearts of Queen
44 Club of Jack
45 Diamonds of Six
46 Spades of Ace
47 Hearts of Ten
48 Hearts of Ace
49 Hearts of Five
50 Hearts of Nine
51 Club of Queen
52 Club of Eight





Testing
---------

Not like other languages, a very small framework is there
<filename>_test.go

$ go test
?    _/Users/namitsha/goProjects/cards [no test files]

func TestNewDeck(t *testing.T) {
t is the testing helper object
*testing.T -- specifies the type of value being passed in the function


deck_test.go
----------------
package main

import "testing"

func TestNewDeck(t *testing.T) {
  d := newDeck()

  if len(d) != 52 {
    t.Errorf("Expected total 52 but got %v", len(d))
  }
}

$ go test
PASS
ok  _/Users/namitsha/goProjects/cards 0.015s



More tests --

deck.go
-------
....
func newDeck() deck {
  cards := deck{} // how to cover all combo
  cardSuits := []string {"Spades", "Diamonds", "Hearts", "Clubs"}
  cardValues := []string {"Ace", "Two", "Three", "Four", "Five" , "Six", "Seven", "Eight", "Nine" , "Ten", "Jack", "Queen", "King" }

  for _, suit := range cardSuits { // instead of i, j use _ so that no compulsion to use
    for _, value := range cardValues {
      cards = append(cards, value + " of " + suit)
    }
  }

  return cards
}
....


deck_test.go
------------

package main

import "testing"

func TestNewDeck(t *testing.T) {
  d := newDeck()

  if len(d) != 52 {
    t.Errorf("Expected total 52, but got %v", len(d))
  }

  if (d[0] != "Ace of Spades") {
    t.Errorf("Expected 1st card as Ace of Spades, but got %v", d[0])
  }

  if d[len(d) - 1] != "King of Clubs" {
    t.Errorf("Expected last card as King of Clubs, but got %v", d[51])
  }

}

func TestSaveToDeckAndNewDeckFromFile (t *testing.T) {
  os.Remove("helloCards")

  deck := newDeck()
  deck.saveToFile("helloCards")

  loadedDeck := newDeckFromFile("helloCards")

  if len(loadedDeck) != 52 {
    t.Errorf("Expected total 52, but got %v", len(loadedDeck))
  }

  os.Remove("helloCards")
}

$ go test
PASS
ok  _/Users/namitsha/goProjects/cards 0.015s



Note: In test cases, if for example we create a test file, we need to take care of the clean up activities as well, other wise it impacts the other test cases!!
Can use Remove() and RemoveAll() functions for the same.

Go Tutorial Part 1 (Basics, Slices, Multi Returns)

Go

                                                                             

Credit goes to - Master the fundamentals and advanced features of the Go Programming Language (Golang), on Udemy by Stephen Grider.
I have tried some snippets on my own and some are referring to the snippets developed while going through the course.


Static Typed languages
While JS, Python etc are dynamic languages having no concern about type of values assigned


Basic Go Types
- - - - - - - - - - - 
bool, string, int, float64
Go doesn’t follow OOPs like Java

 
Steps
- - - - 
Install Go
Install Editor VSCode / Atom, etc/


Hello World
---------------
package main

import "fmt"

func main() {
fmt.Println("hello world")
}


Common commands
------------------------
go build
go run
go fmt
go install
go get
go test

$ go run main.go 
hello world

$ go build main.go 
$ ls
main main.go
$ ./main 
hello world


Packages
------------
2 types - 
 executable : creates and executable file like ./main, name 'main' makes sure its executable, should have a function defined with name as 'main' as well 
 reusable: some helper codes, with any name other than 'main'
 
Imports
----------
fmt - basic library, others are debug, math, cypto, encoding, etc.
 
Functions
------------
func - for function

Variables
------------
  var card string = "hello"
  or 
  card := "hello again"
  card = "new value" // no need for colon as only needed for first time declaration
  
  
package main

import "fmt"

var num int

func main() {
  var card0 string = "Hello World"
  card := "Hello Again World"
  fmt.Println(card0)
  fmt.Println(card)
  fmt.Println(num)
}

$ ./main
Hello World
Hello Again World
0

Note: Can declare a variable outside main(), just cant assign the value


Functions
-------------

func newFunc() string {
  return "new Hello"
}

Note: if datatypes is not defined - too many arguments to return

package main

import "fmt"

var num int

func main() {
  var vari0 string = "Hello World"
  vari1 := "Hello Again World"
  fmt.Println(vari0)
  fmt.Println(vari1)
  fmt.Println(num)

  vari2 := newFunc()
  fmt.Println(vari2)
}

func newFunc() string {
  return "new Hello"
}


Data Structures in Go
----------------------------

Array - Fixed length

Slice - Dynamic Array


Both should have a data type defined, and the elements should be of tne same type

Appending a slice and iterating -

package main

import "fmt"

var num int

func main() {
  varis := []string{"Hello0 ", newFunc()}
  fmt.Println(varis)
  varis = append(varis, "Hello2")
  fmt.Println(varis)

  for i, vari := range varis {
    fmt.Println(i, vari)
  }
}

:= because with every increment, previous value is discarded


Receivers
-------------

stack.go

package main

import "fmt"

type stack[] string

func (s stack) print() {
  for i, str := range s {
    fmt.Println(i, str)
  }
}


main.go

...

func main () {
...
  stacks := stack{"stk1", "stk2", "stk3"}
  stacks = append(stacks, "stk4")

  for i, stack := range stacks {
    fmt.Println(i, stack)
  }
...
}

o/p :
0 stk1
1 stk2
2 stk3
3 stk4



Nested Loops
-----------------

deck.go
- - - - 
func newDeck() deck {
  cards := deck{"Ace of Spades"} // how to cover all combo
  cardSuits := []string {"Spades", "Diamonds", "Hearts", "Club"}
  cardValues := []string {"Ace", "Two", "Three", "Four", "Five" , "Six", "Seven", "Eight", "Nine" , "Ten", "Jack", "Queen", "King" }

  for _, suit := range cardSuits { // instead of i, j use _ so that no compulsion to use
    for _, value := range cardValues {
      cards = append(cards, suit + " of " + value)
    }
  }

  return cards
}

main.go
- - - -
...
  cards = newDeck()
  for i, card := range cards {
    fmt.Println(i, card) // because cards is of type deck, hence print() is available
  }
...

o/p

0 Ace of Spades
1 Spades of Ace
2 Spades of Two
3 Spades of Three
4 Spades of Four
5 Spades of Five
6 Spades of Six
7 Spades of Seven
8 Spades of Eight
9 Spades of Nine
10 Spades of Ten
11 Spades of Jack
12 Spades of Queen
13 Spades of King
14 Diamonds of Ace
15 Diamonds of Two
16 Diamonds of Three
17 Diamonds of Four
18 Diamonds of Five
19 Diamonds of Six
20 Diamonds of Seven
21 Diamonds of Eight
22 Diamonds of Nine
23 Diamonds of Ten
24 Diamonds of Jack
25 Diamonds of Queen
26 Diamonds of King
27 Hearts of Ace
28 Hearts of Two
29 Hearts of Three
30 Hearts of Four
31 Hearts of Five
32 Hearts of Six
33 Hearts of Seven
34 Hearts of Eight
35 Hearts of Nine
36 Hearts of Ten
37 Hearts of Jack
38 Hearts of Queen
39 Hearts of King
40 Club of Ace
41 Club of Two
42 Club of Three
43 Club of Four
44 Club of Five
45 Club of Six
46 Club of Seven
47 Club of Eight
48 Club of Nine
49 Club of Ten
50 Club of Jack
51 Club of Queen
52 Club of King


Slices
-------

Chunks of Slice
slice_temp[0:2] - including 0th but before 2, excluding 2
used for getting subsets

Example using multiple returns
- - - - - - - - - - - - - - - - - - - - - - - - 

deck.go
- - - -

func deal (d deck, handsize int) (deck, deck) { // multiple returns of type deck each
  return d[:handsize], d[handsize:]
}

main.go
- - - - 
...
  fmt.Println("########## sliced decks #########")
  hand, remainingCards := deal(cards, 5) // hand will have first sliced deck and remaining will go in the later

  fmt.Println("hand :: ")
  hand.print()
  fmt.Println("remainingCards :: ")
  remainingCards.print()
...

o/p
hand :: 
0 Ace of Spades
1 Spades of Ace
2 Spades of Two
3 Spades of Three
4 Spades of Four
remainingCards :: 
0 Spades of Five
1 Spades of Six
2 Spades of Seven
3 Spades of Eight
4 Spades of Nine
.......



Watch out for Part 2. 👍 
Regards
TheCodeBuddy









Featured post

Oracle SQL Scheduled Jobs - An Interesting Approach

  Oracle SQL Scheduled Jobs A DB Scheduler is the best way to automate any backend database job. For instance, if you want to process the p...