Big Digits—Two-Dimensional Slices
bigdigits程序(bigdigits/bigdigits.go)从命令行读取一个数字,并且向命令行输出相同的"big"数字。
robin@Robin bigdigits
$ ./bigdigits 2015
222 000 1 55555
2 2 0 0 11 5
2 0 0 1 5
2 0 0 1 555
2 0 0 1 5
2 0 0 1 5 5
22222 000 111 555
bigdigits 程序需要导入4个package
import (
"fmt"
"log"
"os"
"path/filepath"
)
fmt package提供了格式化文本以及读取格式化后的文本函数。log package 提供了日志记录函数。 os package提供了独立于操作系统的变量和函数,包括os.Args([]string slice of strings)用于保存命令输入的参数。 path包中的filepath package 提供了操作文件名和路径的函数(可跨平台).
我们需要一个二维的数据(a slice of sliecs of strings). 以下是我们创建的二维字符串slice. 数字0说明了每个数字在命令行窗口对应行的输出.
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"},
{" 333 ", "3 3", " 3", " 33 ", " 3", "3 3", " 333 "},
{" 4 ", " 44 ", " 4 4 ", "4 4 ", "444444", " 4 "," 4 "},
{"55555", "5 ", "5 ", " 555 ", " 5", "5 5", " 555 "},
{" 666 ", "6 ", "6 ", "6666 ", "6 6", "6 6", " 666 "},
{"77777", " 7", " 7 ", " 7 ", " 7 ", "7 ", "7 "},
{" 888 ", "8 8", "8 8", " 888 ", "8 8", "8 8", " 888 "},
{" 9999", "9 9", "9 9", " 9999", " 9", " 9", " 9"},
}
代码
package main
import (
"fmt"
"log"
"os"
"path/filepath"
)
func main() {
if len(os.Args) == 1 { ➊
fmt.Printf("usage: %s <whole-number>\n", filepath.Base(os.Args[0]))
//usage: bigdigits.exe <whole-number>
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)
}
}
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"},
{" 333 ", "3 3", " 3", " 33 ", " 3", "3 3", " 333 "},
{" 4 ", " 44 ", " 4 4 ", "4 4 ", "444444", " 4 "," 4 "},
{"55555", "5 ", "5 ", " 555 ", " 5", "5 5", " 555 "},
{" 666 ", "6 ", "6 ", "6666 ", "6 6", "6 6", " 666 "},
{"77777", " 7", " 7 ", " 7 ", " 7 ", "7 ", "7 "},
{" 888 ", "8 8", "8 8", " 888 ", "8 8", "8 8", " 888 "},
{" 9999", "9 9", "9 9", " 9999", " 9", " 9", " 9"},
}
程序首先检查是否有输入命令行参数(调用程序后面跟的参数), 如果没有,则log(os.Args)将为1(os.Args[0]保存的是程序的名字, 整个slice的长度至少为1)如第一条语句(➊)所示。在这个例子中,我们通过使用fmt.Printf()函数输出格式化的信息。 它接受 % 占位符,这跟C/C++的printf()函数相似。
path/filepath package提供了路操作函数——比如, filepath.Base()函数返回给定路径的文件基本名(i.e., 文件名). 在输出完使用信息后, 程序使用了os.Exit()函数,并且返回1给操作系统,终止此程序。 在类Unix的系统上,返回0表示成功,非0表示error。
filepath.Base()函数的使用,演示了Go非常好的一个特点:当一个package被导入后。不管它是在顶级或者逻辑上在另一个包中(e.g., path/filepath)。 我们都只需要使用最后的组件(e.g., filepath). 也可以为pacakge给出本地名字,以避免命名冲突。
如果命令行中至少有一个参数,则我们将第一个参数复制给stringOfDigits变量(字符串)。 为了将用户输入的数字转换为big数字,我们必须bigDigits slice 中的每一行, 这样才能产生输出中的行数。换言之,每一行的输出都代表所有数字的最上面部分,然后在输出第二行。 bigDigits中的数字拥有相同的行数,所以我们只需要读取第一个0的行数。
Go的for loop有多种语法,用于不同的目的。在这里,我们使用了 for .. range loops 它反回每一个元素的索引位置。也可以使用以下的语法。
for row := 0; row < len(bigDigits[0]); row++ {
line := ""
for column := 0; column < len(stringOfDigits); column++ {
...
这种语法跟C, C++ and JAVA类似,可for ... range 语法相比来说更简短方便。
每一行的遍历我们都是设置line为空字符。然后我们在遍历stringOfDigits字符串中的每一列(i.e., the characters)。 Go字符串保存的是UTF-8的字节。所以有可能一个字符由两个或者多个字节组成。 但在这里我们不用担心, 数字0,1,...9都是单个字节。跟7-bit的ASCII一样。
当我们在字符串中使用index索引指定位置时,它返回的是这个位置的字节(byte). (byte类型是uint8的同义词). 所以我们将字符串中检索到的字节,减去数字0的字节,得到的就是它代表的数字。在UTF-8(and ASCII)中, '0'字符的字符码(character) 48. 字符'1'的字符码是 49, 以此类推。 所以,如果我们的字符是 '3'(code point 51), 我们可以通过 '3' - '0'(i.e., 51 - 48),它的结果是一个整型(byte) 3。
如果digit(of type byte)在数字范围内,则我们将它添加到变量line后。
如果digit没有在有效的范围内,则调用log.Fatal()函数,输出一个错误信息到os.Stderr。错误信息包含date, time,和一个error. message. 如果没有明确的指明日志输出的目的地。 则调用os.Exit(1)终此程序。我们为什么没有在第一条语句中使用(➊). 是因为我们想要打印出程序的使用信息,而不需要使用到date和time.