0
点赞
收藏
分享

微信扫一扫

go 使用特定的项做搜索

代码文件 main.go 里也有一个init函数,在第 12 行到第 15 行中声明,如代码清单 2-6 所示。  

代码清单 2-6 main.go:第 11 行到第 15 行  

11 // init在main之前调用  

12 func init() {  

13 // 将日志输出到标准输出  

14 log.SetOutput(os.Stdout)  

15 }  

程序中每个代码文件里的init函数都会在main函数执行前调用。这个init函数将标准库里日志类的输出,从默认的标准错误(stderr),设置为标准输出(stdout)设备。在第 7 章,我们会进一步讨论log包和标准库里其他重要的包。

最后,让我们看看main函数第 20 行那条语句的作用,如代码清单 2-7 所示。

代码清单 2-7 main.go:第 19 行到第 20 行  

19 // 使用特定的项做搜索  

20 search.Run("president")  

可以看到,这一行调用了search包里的Run函数。这个函数包含程序的核心业务逻辑,需要传入一个字符串作为搜索项。一旦Run函数退出,程序就会终止。

现在,让我们看看search包里的代码。

2.3  search包

这个程序使用的框架和业务逻辑都在search包里。这个包由 4 个不同的代码文件组成,每个文件对应一个独立的职责。我们会逐步分析这个程序的逻辑,到时再说明各个代码文件的作用。  

由于整个程序都围绕匹配器来运作,我们先简单介绍一下什么是匹配器。这个程序里的匹配器,是指包含特定信息、用于处理某类数据源的实例。在这个示例程序中有两个匹配器。框架本身实现了一个无法获取任何信息的默认匹配器,而在matchers包里实现了 RSS 匹配器。RSS 匹配器知道如何获取、读入并查找 RSS 数据源。随后我们会扩展这个程序,加入能读取 JSON 文档或 CSV 文件的匹配器。我们后面会再讨论如何实现匹配器。  

2.3.1  search.go  

代码清单 2-8 中展示的是 search.go 代码文件的前 9 行代码。之前提到的Run函数就在这个文件里。  

代码清单 2-8 search/search.go:第 01 行到第 09 行  

01 package search  

02  

03 import (  

04 "log"  

05 "sync"  

06 )  

07  

08 // 注册用于搜索的匹配器的映射  

09 var matchers = make(map[string]Matcher)  

可以看到,每个代码文件都以package关键字开头,随后跟着包的名字。文件夹search下的每个代码文件都使用search作为包名。第 03 行到第 06 行代码导入标准库的log和sync包。与第三方包不同,从标准库中导入代码时,只需要给出要导入的包名。编译器查找包的时候,总是会到GOROOT和GOPATH环境变量(如代码清单 2-9 所示)引用的位置去查找。

代码清单 2-9 GOROOT和GOPATH环境变量

GOROOT="/Users/me/go"  

GOPATH="/Users/me/spaces/go/projects" log 包提供打印日志信息到标准输出(stdout)、标准错误(stderr)或者自定义设备的

功能。sync包提供同步 goroutine 的功能。这个示例程序需要用到同步功能。第 09 行是全书第一次声明一个变量,如代码清单 2-10 所示。  

代码清单 2-10 search/search.go:第 08 行到第 09 行  

08 // 注册用于搜索的匹配器的映射  

09 var matchers = make(map[string]Matcher)  

这个变量没有定义在任何函数作用域内,所以会被当成包级变量。这个变量使用关键字var 声明,而且声明为Matcher类型的映射(map),这个映射以string类型值作为键,Matcher 类型值作为映射后的值。Matcher类型在代码文件 matcher.go 中声明,后面再讲这个类型的用途。这个变量声明还有一个地方要强调一下:变量名matchers是以小写字母开头的。

在 Go 语言里,标识符要么从包里公开,要么不从包里公开。当代码导入了一个包时,程序可以直接访问这个包中任意一个公开的标识符。这些标识符以大写字母开头。以小写字母开头的标识符是不公开的,不能被其他包中的代码直接访问。但是,其他包可以间接访问不公开的标识符。例如,一个函数可以返回一个未公开类型的值,那么这个函数的任何调用者,哪怕调用者不是在这个包里声明的,都可以访问这个值。  

这行变量声明还使用赋值运算符和特殊的内置函数make初始化了变量,如代码清单 2-11 所示。  

代码清单 2-11 构建一个映射  

make(map[string]Matcher)  

map是 Go 语言里的一个引用类型,需要使用make来构造。如果不先构造map并将构造后的值赋值给变量,会在试图使用这个map变量时收到出错信息。这是因为map变量默认的零值是nil。在第 4 章我们会进一步了解关于映射的细节。

在 Go 语言中,所有变量都被初始化为其零值。对于数值类型,零值是0;对于字符串类型,零值是空字符串;对于布尔类型,零值是false;对于指针,零值是nil。对于引用类型来说,所引用的底层数据结构会被初始化为对应的零值。但是被声明为其零值的引用类型的变量,会返回nil作为其值。

现在,让我们看看之前在main函数中调用的Run函数的内容,如代码清单 2-12 所示。

代码清单 2-12 search/search.go:第 11 行到第 57 行  

11 // Run执行搜索逻辑  

12 func Run(searchTerm string) {  

13 // 获取需要搜索的数据源列表  

14 feeds, err := RetrieveFeeds()  

15 if err != nil {  

16 log.Fatal(err)  

17 }  

18  

19 // 创建一个无缓冲的通道,接收匹配后的结果  

20 results := make(chan *Result)  

21  

22 // 构造一个waitGroup,以便处理所有的数据源  

23 var waitGroup sync.WaitGroup 24  

25 // 设置需要等待处理  

26 // 每个数据源的goroutine的数量  

27 waitGroup.Add(len(feeds))  

28  

29 // 为每个数据源启动一个goroutine来查找结果  

30 for _, feed := range feeds {  

31 // 获取一个匹配器用于查找  

32 matcher, exists := matchers[feed.Type] 33   if !exists {  

34 matcher = matchers["default"]  

35 }  

36  

37 // 启动一个goroutine来执行搜索  

38 go func(matcher Matcher, feed *Feed) {  

39 Match(matcher, feed, searchTerm, results)  

40 waitGroup.Done()  

41 }(matcher, feed)  

42 }  

43  

44  // 启动一个goroutine来监控是否所有的工作都做完了 45  go func() {  

46 // 等候所有任务完成  

47 waitGroup.Wait()  

48  

49 // 用关闭通道的方式,通知Display函数  

50 // 可以退出程序了  

51 close(results)  

52 }()  

53  

54 // 启动函数,显示返回的结果,并且  

55 // 在最后一个结果显示完后返回  

56 Display(results)  

57 }  

Run函数包括了这个程序最主要的控制逻辑。这段代码很好地展示了如何组织 Go 程序的代码,以便正确地并发启动和同步 goroutine。先来一步一步考察整个逻辑,再考察每步实现代码的细节。  

先来看看Run函数是怎么定义的,如代码清单 2-13 所示。

代码清单 2-13 search/search.go:第 11 行到第 12 行  

11 // Run 执行搜索逻辑  

12 func Run(searchTerm string) {  

Go 语言使用关键字func声明函数,关键字后面紧跟着函数名、参数以及返回值。对于Run 这个函数来说,只有一个参数,是string类型的,名叫searchTerm。这个参数是Run函数要搜索的搜索项,如果回头看看main函数(如代码清单 2-14 所示),可以看到如何传递这个搜索项。  

举报

相关推荐

java搜索目录特定文件夹

0 条评论