Go Practice Questions (Solutions)

  1. Using English plus simple code examples, explain the difference between arrays and slices in Go.

    Sample solution:

    In Go, an array is sequence of 0 or more objects stored contiguously and efficiently accessible by their position, e.g.:

    a := [3]float64{1, 2, 3}   // array of 3 floats
    

    Importantly, in Go the length of an array is part of the array’s type. That means arrays with different lengths are incompatible. For instance, this is okay in Go:

    a := [3]float64{1, 2, 3}
    var b [3]float64
    
    b = a   // okay: copies a into a
    
    var c [4]float64   // length 4, so c is not same type as a and b
    
    c = a   // compile-time error: a and c are different type
    

    A slice is an object that describes a contiguous sub-part of an array. For example:

    sl := []float64{1, 2, 3}  // a slice
    

    The size of the slice is not specified here because slices do not have a fixed size. Instead, they can grow and shrink in size.

    Slices always refer to an underlying array, i.e. slices are “backed” by an array. The built-in copy function can be used to make a copy of a slice, and the built-in append function can be used to add a new element to the end of a slice.

  2. Using English plus a simple code example, give a major reason why Go does not let you pass a slice to a function that expects an array.

    Sample solution:

    You can’t pass a slice to a function that expects an array because the slice might not be the same size as the array. For example:

    func sum(p [3]float64) float64 {
      return p[0] + p[1] + p[2]
    }
    
    // ...
    
    sl := []{5, 6}  // a slice of length 2
    

    If Go let you call sum(sl), then there would be an error when it tried to access p[2], because there is no position 2 in the array.

  3. Write a function called vc_count(s string) that returns the number of vowels and consonants in s.

    A vowel is defined to be one of these lowercase letters: a, e, i, o, u, y. A consonant is defined to be any character that is not a vowel, and also the letter y. Thus y should be counted as both a vowel and a consonant.

    Also, you must use a ranged for-loop (in a sensible way) in your solution.

    Your function must work with code like this:

    v, c := vc_count("yay")
    fmt.Println(v, c)
    

    Sample solution:

    func isVowel(c rune) bool {
      return c == 'a' || c == 'e' || c == 'i' ||
              c == 'o' || c == 'u' || c == 'y'
    }
    
    func vc_count(s string) (int, int) {
      vowels, cons := 0, 0
      for _, ch := range s {
        if isVowel(ch) {
          vowels++
        }
        if ch == 'y' || !isVowel(ch) {
          cons++
        }
      }
      return vowels, cons
    }
    
  4. Write a goroutine called char_gen(s string, out chan rune) that returns the characters of s one at a time on channel out. Use the built-in function close to close out when all the characters have been sent.

    Demonstrate how to use char_gen by writing a main function that uses it print out an entire string.

    Sample solution:

    func char_gen(s string, out chan rune) {
      for _, c := range s {
        out <- c
      }
      close(out)
    }
    

    One way to write main:

    func main() {
      nextChar := make(chan rune)
      s := "applesauce"
      go char_gen(s, nextChar)
      for i := 0; i < len(s); i++ {
        fmt.Println(string(<-nextChar))
      }
    }
    

    An alternate way using a ranged for-loop:

    func main() {
      nextChar := make(chan rune)
      s := "applesauce"
      go char_gen(s, nextChar)
      for ch := range nextChar {
        fmt.Println(string(ch))
      }
    }
    

    This second version only works if char_gen closes the channel when its done. Try commenting-out close(out) in char_gen to see what happens.

  5. Answer all the following questions with Go code.

    1. Write a function called add(x, y) that returns the sum of two ints, e.g.:

      fmt.Println(add(3, 5));  // 8
      

      Solution:

      func add(x, y int) int {
        return x + y
      }
      
  1. Write a function called c_add that is a curried version of add, e.g.:

    add3 := c_add(3)
    fmt.Println(add3(5))      // 8
    
    fmt.Println(c_add(2)(4))  // 6
    

    Solution:

    func c_add(x int) func(int) int {
      f := func(y int) int {
        return x + y
      }
      return f
    }
    
  2. Write a function called curry(f) that returns a curried version of f. Assume that f is a (non-curried) function that takes two ints as input and returns an int.

    It can be used like this:

    curr_add := curry(add)  // add is the regular non-curried add function
    add2 := curr_add(2)
    fmt.Println(add2(6))    // 8
    

    Solution:

    func curry(f func(int, int) int) func(int) func(int) int {
        g := func(x int) func(int) int {
            h := func(y int) int {
                return f(x, y)
            }
            return h
        }
        return g
    }
    
  1. a) Write a function called negpos(s) that takes a slice of ints as input, and returns two new slices, named neg and pos, where neg contains all the numbers in s that are less than 0, and pos contains all the numbers in s that are greater than, or equal to, 0. For example:

    s := []int{6, -2, 0, 2, -6, -4, 3}
    neg, pos := negpos(s)
    fmt.Println(neg)   // {-2, -6, -4}
    fmt.Println(pos)   // {6, 0, 2, 3}
    

    Use features of Go to make negpos as short and clear as possible.

    Sample solution:

    func negpos(s []int) (neg, pos []int) {
      for _, n := range s {
        if n < 0 {
          neg = append(neg, n)
        } else {
          pos = append(pos, n)
        }
      }
      return neg, pos
    }
    

    b) To help test your negpos function, write a function called all(s, pred) where:

    • s is a slice of int values
    • pred is a function that takes one int as input, and returns a bool

all(s, pred) returns true if every int in s is true for pred, and false otherwise (i.e. it returns false if s contains 1, or more, int` values that cause ``pred to return false).

Sample solution:

func all(s []int, pred func(int) bool) bool {
  for _, n := range s {
    if !pred(n) {
      return false
    }
  }
  return true
}

c) Write a single Go statement of the form all(s, pred) that returns true just when every element in s is less than 0, and false otherwise.

Sample solution:

all(neg, func(n int) bool { return n < 0 })