Go - if/else, switch, Loops, I/O, and Functions

Contemporary Programming Languages - CS2001 - 2 November 2017

if/else

package main

import (
  "fmt"
  "math"
)

func main() {
  x, y := 2, 1

  // The condition must be a bool!
  /// If it's not, it's a compiler error.
  if x > y {
    fmt.Println("Yay")
  }

  a, b := 1, 2
  if a == b {
    fmt.Println("Equal")
  } else {
    fmt.Println("Not equal")
  }

  myString := "submarine"
  if myString == "frog" {
    fmt.Println("Yay!")
  } else if myString == "submarine" {
    fmt.Println("Meh")
  }

  if val := math.Floor(12.5); val > 10 {
    // We can only access val in here
    fmt.Println(val)
  }
  // Can't access val anymore!
}

switch case

package main

import "fmt"

func f() int {
  fmt.Println("f!")
  return 1
}

func getFavoriteThing() string {
  return "cookies"
}

func main() {

  ////////////////////////////////
  // A basic switch
  ////////////////////////////////

  animal := "pony"
  switch animal {
  case "frog":
    fmt.Println("Ribbit")
  case "pony":
    fmt.Println("Neigh")
    fallthrough // We use fallthrough to fall through to the next case
  case "dog":
    fmt.Println("Bark")
  default:
    fmt.Println("Noise")
  }

  ///////////////////////////////
  // Using a simple statement
  ///////////////////////////////

  switch fave := getFavoriteThing(); fave {
  case "cookies":
    // We have access to fave in the switch
    fmt.Println(fave)
  default:
    fmt.Printf("WRONG! Should have been %s", fave)
  }
  // We no longer have access to fave out here.

  ///////////////////////////////
  // You can make function calls in your cases...
  // As long as the types agree
  ///////////////////////////////

  i := 1
  switch i {
  case 0:
    fmt.Println("Case 0")
  case f():
    fmt.Println("Case f()")
  }

  /////////////////////////////
  // By default, a switch will switch on true.
  // This is an idiomatic alternative to a big if-else chain.
  /////////////////////////////

  x, y := 42, 42
  switch {
  case x > y:
    fmt.Println("Case x > y")
  case x < y:
    fmt.Println("Case x < y")
  default:
    fmt.Println("Case x == y")
  }
}

Looping

  • There is only for.
  • Counting loop
    acc := 1
    for i := 0; i < 10; i++ {
      acc += i
    }
    
  • Sentinal loop
    for x < 100 {
      // stuff
    }
    
  • Infinite Loop
    for {
      // stuff
    }
    
  • Range
    animals := [3]string {"frog", "giraffe", "submarine"}
    for index, element := range animals {
      fmt.Println(index, element) // 0 frog 1 giraffe 2 submarine
    }
    
    • Index only
      for index := range animals {
        fmt.Println(index)
      }
      
    • Element only
      for _, element := range animals {
        fmt.Println(element)
      }
      

Reading Files

  • There are seeral ways to read files in Go
  • Choice depends on your application
package main
import(
  "fmt"
  "io/ioutil"
  "os"
)
func main() {
  contents, err := ioutil.ReadFile("file.txt")
  if err != nil {
    fmt.Println(err)
    os.Exit(1)
  }
  fmt.Println(string(contents)) // contents is slice of byte
}

Functions

  • Functions can take zero or more args
  • Types come after the variable names
  • Return type comes at the end
  • Arguments are pass by value
package main
import "fmt"

func add(x int, y int) int { // can also be func add (x, y int) int {}
  return x + y
}

func main() {
  fmt.Println(add(3,5))
}
  • Multiple results
    func swap(x, y string) (string, string) {
      return y, x
    }
    
    func main() {
      a, b := swap("hello", "world")
      fmt.Println(a, b) // world hello
    }
    
  • Named Results
    func f(val int) (x, y int) {
      x = val * 4 / 9
      y = val - x
      return // naked return, if not done, compiler error
    }
    
  • Defer
    • A defer statement defers/ puts off the execution of a function until the surrounding function returns
    • The deferred calls arguments are evaluated immediately, but the function call is not executed until the surrounding function returns
    func main() {
      defer fmt.Println("world")
      fmt.Println("hello")
    }
    // Outputs hello immediately
    // Outputs world after main() ends
    
    func f() string {
      fmt.Println("Beep")
      return "world"
    }
    
    func main() {
      defer fmt.Println(f())
      fmt.Println("hello")
    }
    /* Output
         Beep
         hello
         world */
    
    • Stacking Defers
      func main() {
        fmt.Println("counting")
        for i := 0; i < 10; i++ {
          defer fmt.Println(i)
        }
        fmt.Println("done")
      }
      /* Output
           counting
           done
           9
           8
           7
           6
           5
           4
           3
           2
           1
           0 */
      
      • Realistic example
        func main() {
          tempfile, err := ioutil.TempFile("", "example")
          if err != nil {
            log.Fatal(err)
          }
          defer os.Remove(tempfile.Name())
          // Use tempfile here
        }
        

Map

  • Maps keys to values
  • Kind of like a dictionary in Python
  • Zero value is nil
  • Cannot set or get values from nil
  • len() will give the number of items (key/value pairs) in the map
  • cap() doesn’t work
var sounds map[string]string
sounds = make(map[string]string)
weights := make(map[string]float64)

sounds["frog"] = "ribbit"
weights["frog"] = 2.4

fmt.Println(weights) // map[frog: 2.4]
  • Map Literals
    counts := map[string]int {"frog": 2, "submarine": 1}
    names := map[string][]string {
      "frog": []string {"jim", "fred"},
      "submarine": []string {"bob"},
    }
    
    names["frog"] // {"jim", "fred"}
    names["cat"] // nil (zero value for []string)