1.概述
在main/wc.go中有空的mapF()和reduceF()函数,Part II的内容就是实现这两个函数,以统计每个单词出现的次数,区分大小写。替换掉Part I中的MapFunc和ReduceFunc。
在~/6.824/src/main中有一些名称类似pg-*.txt的文件,这便是Part II要处理的输入文件,运行下列命令来测试程序
cd 6.824
export "GOPATH=$PWD"
cd "$GOPATH/src/main"
go run wc.go master sequential pg-*.txt
最终的输出文件是mrtmp.wcseq,若程序编写正确,最终读取mrtmp.wcseq可以得到如下结果
sort -n -k2 mrtmp.wcseq | tail -10
that: 7871
it: 7987
in: 8415
was: 8578
a: 13382
of: 13536
I: 14296
to: 16079
and: 23612
the: 29748
2.mapF()
对每个输入文件会调用一次mapF,第一个参数是文件名,第二个参数是文件的内容,最后返回键值对切片。
func mapF(filename string, contents string) []mapreduce.KeyValue {
// Your code here (Part II).
}
返回的键值对切片中,Kay存放的是单词,Value存放的是这个单词在这篇文章中出现的次数。使用strings.Fields方法对输入文件的内容进行分割.还有需要注意的一点是,需要将标点符号进行替换,以免在按间隔符分割得到单词后,由于存在标点符号问题不能匹配成功。实现如下(ps.比较好的方式是使用strings.FieldsFunc):
func mapF(filename string, contents string) []mapreduce.KeyValue {
//删除,.?等等标点符号
re, _ := regexp.Compile("[^a-z^A-Z]")
contents = re.ReplaceAllString(contents, " ")
var kv map[string]int
kv = make(map[string]int)
words := strings.Fields(contents)
for _, w := range words {
if _,ok := kv[w];ok {//word已经存在,计数累加
kv[w] +=1
}else {
kv[w] =1
}
}
//转换为[]mapreduce.KeyValue
var res []mapreduce.KeyValue
for k,v := range kv {
res = append(res,mapreduce.KeyValue{k,strconv.Itoa(v)})
}
return res
}
3.reduceF()
reduceF()对map tasks产生的每一个key都调用一次,values存放的是key在不同输入文件中出现的次数.在reduceF将values都相加然后返回就是每个单词在所有文件中出现的次数。
func reduceF(key string, values []string) string {
// Your code here (Part II).
}
实现如下
func reduceF(key string, values []string) string {
total := 0
for _,v := range values {
i, err := strconv.Atoi(v)
if err!=nil {
fmt.Println(err)
}
total += i
}
return strconv.Itoa(total)
}
4.测试运行
cd "$GOPATH/src/main"
time go run wc.go master sequential pg-*.txt
出现类似下面结果运行成功
查看生成的结果文件mrtmp.wcseq,能与下面内容一致则说明程序运行正确sort -n -k2 mrtmp.wcseq | tail -10
that: 7871
it: 7987
in: 8415
was: 8578
a: 13382
of: 13536
I: 14296
to: 16079
and: 23612
the: 29748
或者直接运行这个脚本测试
bash ./test-wc.sh