文章标签 » Golang

MIT 6.824 (2022) Lab 1: MapReduce解题思路与问题总结

写在前面:

  • 尊重课程的要求,未公开源码。
  • 本文主要是给有需要的小伙伴一些提示,避开一些弯路和坑。
  • 只是分享我的思路,我的解题方案。一家之言,难免有错漏,请多指教。

如果你问我做题花了多久,我只能回答很久很久:思考怎么开始动手花了很久;调试花了很久。
但我想,小伙伴们应该跟我一样,主要目的是学习,而不是为了交作业。所以,多长时间不重要,有没有收获才重要。

第一只拦路虎

按照课程说明(见文末链接:说明与提示,下文简称“说明与提示”),执行第一个go命令go build -race -buildmode=plugin ../mrapps/wc.go就失败了:-buildmode=plugin not supported on windows/amd64
有点挫败,也有点失望,对Windows失望。

刚好近期学习了Docker,试验下来发现Docker+MIT6.824课程简直绝配:

  • 编译环境随便选,即使你在Windows上开发,也可以让你的代码在Ubuntu环境下编译和运行
  • 源码下载下来,相同包下的相同函数会编译报错(GoLand),相同的目录下(mrapps)有多个main函数也会报错。这样就无法在GoLand中调试代码

Continue Reading »

MIT 6.824 (2022) Lab 1: MapReduce

写在前面:这篇实验课的说明(原版)和代码前前后后看了很多遍,还是一团糨糊,不知从何入手。看到后面,又忘记了前面。于是想把它翻译一遍,希望能把题理解得更透彻点。果然,翻译过程中发现几个之前忽略的点。

原文地址:https://pdos.csail.mit.edu/6.824/labs/lab-mr.html

简介

在这节实验课里你将构建一个MapReduce系统。你需要实现一个工作程序(worker process)和一个调度程序(coordinator process)。工作程序用来调用Map和Reduce函数,并处理文件的读取和写入。调度程序用来协调工作任务并处理失败的任务。你将构建出跟 这篇MapReduce论文 里描述的类似的东西。(注意:本实验中用”coordinator”替代里论文中的”master”。)

开始

首先需要搭配好Go的开发环境,无需赘述。 接着可以通过git来获取本实验所需的初始代码:

git clone git://g.csail.mit.edu/6.824-golabs-2022 6.824

我们在src/main/mrsequential.go提供了一个简单的MapReduce实现。它在同一个进程里执行里Map函数和Reduce函数。我们也提供了一些MapReduce的应用程序:单词计数器(mrapps/wc.go)和文本检索器(mrapps/indexer.go)。你可以通过以下命令来执行单词计数器程序:

cd 6.824
cd src/main
go build -race -buildmode=plugin ../mrapps/wc.go
go run -race mrsequential.go wc.so pg*.txt
more mr-out-0

注意:-race选项打开了Go语言的竞争探测器。我们建议你在开发和调试6.824实验代码时都加上这个选项。我们在评分时不会使用这个选项。尽管如此,如果你的代码里存在竟态,我们测试时不用竟态探测器它也会失败。

mrsequential.go将其输出保存在mr-out-0文件中。输入来自pg开头的txt文件。

可以随意从mrsequential.go中借用代码。你也应该打开mrapps/wc.go看看MapReduce的应用程序是如何实现的。

Continue Reading »

原来这就是表驱动开发

现在需要你提供这样一个函数:获得指定月份的天数(为了方便讨论,忽略闰年)。你会怎么做?
大概率是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
func DaysOfMonthGeneral(month int) int {
	switch month {
	case 1:
		return 31
	case 2:
		return 28
	case 3:
		return 31
	case 4:
		return 30
	case 5:
		return 31
	case 6:
		return 30
	case 7:
		return 31
	case 8:
		return 31
	case 9:
		return 30
	case 10:
		return 31
	case 11:
		return 30
	case 12:
		return 31
	default:
		panic("invalid month")
	}
}

表驱动开发则是这样的:

1
2
3
4
5
var daysPerMonth = []int{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}

func DaysOfMonth(month int) int {
	return daysPerMonth[month-1]
}

这个例子太简单?那再看个例子。

根据分数得到等级。

分数 等级
>=90 A
<90 B
<75 C
<65 D
<50 F

你很可能会这么做:

1
2
3
4
5
6
7
8
9
10
11
12
13
func GetGradeGeneral(score float32) string {
	if score >= 90.0 {
		return "A"
	} else if score >= 75.0 {
		return "B"
	} else if score >= 65.0 {
		return "C"
	} else if score >= 50.0 {
		return "D"
	} else {
		return "F"
	}
}

有没有更灵活的方式呢?

有的,还是表驱动法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var (
	rangeUpper = []float32{50.0, 65.0, 75.0, 90.0, 100.0}
	grades     = []string{"F", "D", "C", "B", "A"}
)

// GetGrade 表驱动法
func GetGrade(score float32) string {
	maxGradeLevel := len(grades) - 1

	gradeLevel := 0
	studentGrade := "A"
	// 数据量大时可考虑二分查找
	for studentGrade == "A" && gradeLevel < maxGradeLevel {
		if score < rangeUpper[gradeLevel] {
			studentGrade = grades[gradeLevel]
		}
		gradeLevel++
	}
	return studentGrade
}

总结

表驱动,就是把复杂的条件分支或运算逻辑转移到对“表”的访问上。

这里说的“表”和数据库中的表没有关系,更像是一张表格或者容器。通常可以用数组或者Map实现。