Assignment 5: Practice with Go

The following questions are meant as practice for learning some of the basic features of Go. Write all your solution code in a single file named a5.go, in a package named main.

Each question is out of 2 marks: 1 for correctness, and 1 for code style and readability. Readability includes using Go features in an appropriate way.

You may import any standard Go packages. Write all other code yourself: don’t use any packages or code from outside of the Go standard library. Please clearly cite the source of any ideas you used in this work.

Please submit your final a5.go file on Canvas.

  1. (2 marks) Implement a function called countEmirpsLessThan(n) that returns the number of emirps less than the int n. An emirp is prime number that is a different prime when you reverse its digits. For example, the emirps less than 100 are 13, 17, 31, 37, 71, 73, 79, and 97, and so countEmirpsLessThan(100) should return 8.

    Note that primes such as 11 and 101 are not emirps since there reversals are not different primes.

  2. (2 marks) Implement the following function:

    func countWords(filename string) (map[string]int, error)
    

    countWords(filename) reads the contents of the text file named filename and returns two things:

    • A map[string]int whose keys are all the different words in the file, and whose values are the number of times the corresponding key occurs in the file.
    • An error code that is nil if the file was successfully opened and read. If the file cannot be read (e.g. it doesn’t exist), then the returned map should be nil and the error should be the error returned by your file-reading function.

    For this questions, words are defined to be strings separated by either a space or a newline character.

    For example, suppose you have the text file sample.txt contains this:

    The big big dog
    ate the big apple
    

    Then if you call:

    results, err := countWords("sample.txt")
    // ...
    

    err should be nil, and results should be:

    map[The:1 big:3 dog:1 ate:1 the:1 apple:1]
    

    Note that case matters: strings like the and The are considered different strings.

  3. (2 marks) Here’s a simple way to represent a moment in time:

    type Time24 struct {
        hour, minute, second uint8
    }
    // 0 <= hour < 24
    // 0 <= minute < 60
    // 0 <= second < 60
    

    Implement the following functions and methods:

    • The function equalsTime24(a, b) returns true if a and b are exactly the same time, and false otherwise.

    • The function lessThanTime24(a, b) returns true, if time a comes strictly before b, and false otherwise.

    • The method (not a function!) t.String() that converts a t to human-readable string. It has this signature:

      func (t Time24) String() string {
          // ...
      }
      

      The returned string should have the form “hh:mm:ss”. For example:

      t := Time24{hour: 5, minute: 39, second: 8}
      fmt.Println(t)  // "05:39:08"
      

      Notice that each number is written as 2-digits, possibly with a leading 0. Thus the returned string will always be the same length.

      Also, notice that you don’t need to call t.String() inside fmt.Println. That’s because the signature for String implements the fmt.Stringer interface, and functions like fmt.Print know to call String() on objects that implement it.

    • The method (not a function!) t.validTime24() returns true if t is a valid Time24 object (i.e. it meets the constraints listed in the comments below the struct), and false otherwise.

    • The function minTime24, which returns the smallest time in a slice of Time24 objects. It has this signature:

      func minTime24(times []Time24) (Time24, error)
      

      If times is empty, then Time24{0, 0, 0} is returned, along with an error object with a helpful message.

      If times has one, or more, items, then the smallest time is returned, and the error is nil.

  4. (2 marks) Write a function called linearSearch(x, lst) that uses linear search to return the first index location of x in the slice lst. It must work with (at least) both strings and ints as in these examples:

    • linearSearch(5, []int{4, 2, -1, 5, 0}) returns 3
    • linearSearch(3, []int{4, 2, -1, 5, 0}) returns -1 (because 3 is not in the slice)
    • linearSearch("egg", []string{"cat", "nose", "egg"}) returns 2
    • linearSearch("up", []string{"cat", "nose", "egg"}) returns -1

    You can use helper functions, but you must have only one function named linearSearch. If the type of x is not the same as the type of the elements in lst, then linearSearch should call panic with a helpful message.

  5. (2 marks) Implement a function the returns all bit sequences of length n. It should have this signature:

    func allBitSeqs(n int) [][]int
    

    A bit sequence is a slice of ints that contains only 0s and 1s.

    For example:

    • allBitSeqs(1) returns [[0] [1]].
    • allBitSeqs(2) returns [[0 0] [0 1] [1 0] [1 1]].
    • allBitSeqs(3) returns [[0 0 0] [0 0 1] [0 1 0] [0 1 1] [1 0 0] [1 0 1] [1 1 0] [1 1 1]].

    The exact order of the slices in the returned [][]int doesn’t matter. So, for example, allBitSeqs(2) could instead return [[1 1] [1 0] [0 0] [0 1]].

    If n <= 0, then return an empty [][]int.

    Of course, for large values of n it allBitSeqs will take too much time and memory to actually run. Nonetheless, the algorithm you use to calculate the bit sequences should work with any int greater than 0, if given enough time and memory.