Another Example—Two-Dimensional Slices
The bigdigits program described next reads a number entered on the command line (as a string), and outputs the same number onto the console using “big” digits. Back in the twentieth century, at sites where lots of users shared a high-speed line printer, it was common practice for each user's print job to be preceded by a cover page that showed some identifying details such as the username and the name of the file being printed, using this kind of technique.
I'll review the code in three parts: first the imports, then the static data, and then the processing. But right now, let's look at a sample run to get a feel for how it works:
$ ./bigdigits 290175493
Each digit is represented by a slice of strings, with all the digits together represented by a slice of slices of strings. Before looking at the data, here is how we could declare and initialize single-dimensional slices of strings and numbers:
longWeekend := []string{"Friday", "Saturday", "Sunday", "Monday"}
var lowPrimes = []int{2, 3, 5, 7, 11, 13, 17, 19}
Slices have the form []Type, and if we want to initialize them we can immediately follow with a brace-delimited, comma-separated list of elements of the corresponding type. We could have used the same variable declaration syntax for both, but have used a longer form for the lowPrimes slice to show the syntactic difference and for a reason that will be explained in a moment. Since a slice's Type can itself be a slice type we can easily create multidimensional collections (slices of slices, etc.).
The bigdigits program needs to import only four packages.
import (
"fmt"
"log"
"os"
"path/filepath"
)
The fmt package provides functions for formatting text and for reading formatted text. The log package provides logging functions. The os package provides platform-independent operating-system variables and functions including the os.Args variable of type []string (slice of strings) that holds the command-line arguments. And the path package's filepath package provides functions for manipulating filenames and paths that work across platforms. Note that for packages that are logically inside other packages, we only specify the last component of their name (in this case filepath) when accessing them in our code.
For the bigdigits program we need two-dimensional data (a slice of slices of strings). Here is how we have created it, with the strings for digit 0 laid out to illustrate how a digit's strings correspond to rows in the output, and with the strings for digits 3 to 8 elided.
var bigDigits = [][]string{
{" 000 ",
" 0 0 ",
"0 0",
"0 0",
"0 0",
"0 0",
" 000 "},
{" 1 ", "11 ", " 1 ", " 1 ", " 1 ", " 1 ", "111"},
{"222","2 2"," 2"," 2 ","2 ","2 ","22222"},
// ... 3 to 8 ...
{" 9999", "9 9", "9 9", " 9999", " 9", " 9", " 9"},
}
Variables declared outside of any function or method may not use the := operator, but we can get the same effect using the long declaration form (with keyword var) and the assignment operator (=) as we have done here for the bigDigits variable (and did earlier for the lowPrimes variable). We still don't need to specify bigDigits' type since Go can deduce that from the assignment.
We leave the bean counting to the Go compiler, so there is no need to specify the dimensions of the slice of slices. One of Go's many conveniences is its excellent support for composite literals using braces, so we don't have to declare a data variable in one place and populate it with data in another.
The main() function that reads the command line and uses the data to produce the output is only 20 lines.
func main() {
if len(os.Args) == 1 {
fmt.Printf("usage: %s <whole-number>\n", filepath.Base(os.Args[0]))
os.Exit(1)
}
stringOfDigits := os.Args[1]
for row := range bigDigits[0] {
line := ""
for column := range stringOfDigits {
digit := stringOfDigits[column] - '0'
if 0 <= digit && digit <= 9 {
line += bigDigits[digit][row] + " "
} else {
log.Fatal("invalid whole number")
}
}
fmt.Println(line)
}
}


