教学大纲


一个功能完整的 Exercism 学习路径包含两类练习:概念练习和实践练习。 两者本质不同,却能很好地互补。

学习路径的概念练习是为教授一个个概念而设计的练习,这些概念构成某门编程语言的基础。 它们共同构成一份_教学大纲_。

本文档提供一些指引和技巧,帮助你为自己的学习路径成功设计教学大纲。

教学大纲的目标

教学大纲的最终目标,是让学生能够自如地读写目标语言中地道的代码。

每一道概念练习都极其聚焦。 它是朝着理解这门语言的某个方面迈出的一小步,目标十分明确。 它只建立在之前已经引入的概念之上。

通过解这道练习,学生开始熟悉这个概念。 理解主要来自动手做,讲解起到的作用要小得多。 讲解内容的作用是介绍学生解出这道练习所需的那些想法(这也是文件名叫 introduction.md 的原因)。

我们希望让学生能够立刻开始写代码,而不必事先理解所有东西。 为此,我们会含糊带过一些细节,把很多事情留作未解。 我们尽量简化,并在可能的地方提供代码桩。 这能减轻起步时的认知负担,也为知识慢慢沉淀留出时间和空间。 采取这种做法,并不是说学生不需要了解这些东西,而是说他们暂时还不需要了解。

最早的几道练习往往需要包含不地道的代码。 因为一开始,学生对这门语言的大部分内容都不了解,大多数概念也还没有引入。 允许最早的练习里出现不地道的代码,学生就能在熟悉的领域里迈出许多小步,而不是在陌生的领域里迈出寥寥几大步。 结果是,他们能更快、更少阻力地到达写出地道代码的阶段。

基本结构

练习按树状结构组织,位于顶部的入门练习是起点。 后面的练习所教授的概念,都依赖于此前已经理解的概念。

移植与借鉴

不妨看看其他语言的学习路径是怎样搭建概念练习的。 你可以在这里找到其他语言学习路径的概念练习示例。

不过,如果你打算以其他练习作为自己练习的起点,务必确保最终的练习讲的是这个概念在你所用语言中的样子。 有时概念之间差别细微,有时则天差地别。 有时概念在其他语言里根本不存在。

教学大纲,也就是概念树,应当体现这门具体语言中实际存在的概念。

不要仅仅因为其他学习路径有某个概念就把它加进来。

有时,某个概念在这门语言里并不存在,但人们经常要用已有的概念绕过它,于是你可能会很想把它加进来。 不要这样做,而要引入这门语言_确实_会用的概念,并考虑加一道练习,讲清楚在这种场合下该怎么用它。

例如,Go 里没有枚举。 Go 的概念树改为引入常量,并教你如何在你可能用枚举的那种场合下使用常量。

寻求帮助

需要帮助时不要犹豫。 一开始就提问,或者在写练习的过程中提问,都比等到代码审查时才讨论要好。

在 GitHub 上,你可以 @ 一下 @exercism/learning-mode 团队。 在 Exercism 论坛上,到 Exercism Support 分类开一个 issue。

入门

我们的经验表明,开发教学大纲最务实的做法是让概念树自然生长,从最简单的概念开始。 我们不必事先设计好一切,而且一般说来,不要想得太远反而更有用。

我们从最少的概念开始,也就是用这门语言写任何东西都最基础的那些概念。 我们也尽量从普通开发者最熟悉的概念开始。 熟悉是好事。 熟悉就不会让人困惑。

记住,虽然终点是写出地道的代码,但通向终点的垫脚石并不总是地道的。 使用熟悉的东西(即便它并不是这门语言的优秀代码示例),能帮助学生更快地朝着写出更符合该语言典型风格的代码这一目标前进。

开发第一道练习

不要试图一开始就画出整棵概念树,先从第一道练习开始就好。 第一道练习的目标,是让学生以尽可能小的阻力开始学习。 他们正在迈出熟悉这门语言代码样貌的第一步。 他们可能会写一小段代码,或者只是往代码桩里补上几处内容来完成练习。 为了进入这道练习,学生已经解决过“Hello, World!”了。 但在“Hello, World!”里,他们改的只是措辞。 这门语言的全部语法对他们来说可能仍然陌生。 尽量围绕快速取得成果来优化,让学生对最基本的语法足够熟悉,从而能自信地往前走。

进一步了解如何开发第一道练习。

接下来的练习

第一道练习应当解锁几道引入基础概念的练习。 这些概念会是原始类型或基本类型,以及对这些类型的简单操作之类的东西。

进一步了解如何开发接下来的练习。

然后呢?

到了这一步,事情往往就开始变得有意思了。 此时你_可以_引入的东西实在太多了。 你该怎么决定接下来处理哪些概念?

其实这不太重要。 只要从某个看起来合理的地方开始,就没问题。

进一步了解在我们看来,扩展概念树时“合理”意味着什么。

不要把实践练习改成概念练习

好的概念练习极其聚焦,理想情况下只教一个概念。 解它的方法通常只有一种预期解法。 这与实践练习形成对比:实践练习是开放式的,适合自由探索。

好的概念练习通常是糟糕的实践练习,反之亦然。 由于实践练习和概念练习的目标完全不同,我们不会把实践练习改造成概念练习。 我们所有的概念练习都是从零写起,或者以专门为教授简单概念而创作的故事为基础。

我们鼓励先含糊带过

有时你会觉得陷入了死结。 概念 A 需要先理解概念 B,而概念 B 又需要先理解概念 A。

这时就简化。 先把其中一个的某些复杂之处含糊带过,好让学生先熟悉另一个。 完全可以说明某个东西之后会更深入地介绍,而眼下学生只需要理解这一小点。

概念是分阶段、随着时间推移被理解得越来越深入的。

选择故事

概念练习总是有故事的。

如果你是从其他学习路径 fork 一道练习,那这道练习本身就已经有故事了。 那样的话就没问题了。

想知道有哪些现成的故事可以用、哪些练习可以 fork,请查看故事列表。

如果你有了概念却没有故事,我们的建议是写一段小而易懂的代码示例,用上你正在引入的概念。 然后倒过来,围绕这段代码编一个故事。 故事要简单到不能再简单。 它不必是一篇好小说。 不需要有强烈的情节或人物塑造。 几行字就够了。

有了故事的点子,可以和 Exercism 团队碰一碰。 我们在构思合适的故事方面经验丰富。

有了故事之后,你很可能需要稍微调整代码,让它与故事契合。

来自社区的贡献

做教学大纲涉及两项彼此独立又相互交织的工作:

  1. 教学大纲设计:挑选概念并排定顺序
  2. 练习实现:编写文档、创建练习来讲授这些概念

我们发现,让更广泛的社区参与练习实现,既有趣又让人收获良多。 而教学大纲设计本身,则更容易由一小队贡献者共同完成,他们都在努力理解整份教学大纲及其各种细枝末节。

话虽如此,我们建议教学大纲设计团队先实现前 5 个或 6 个概念,再向社区开放贡献。 这有助于确保教学大纲设计核心团队的成员,在不得不审阅来自更广泛社区的拉取请求之前,自己已经理解了这个流程。

为这些更高阶的概念创建 issue 也更容易,社区成员做起来往往也更有意思,因为需要考虑的约束更少。

创建 issue

我们至今还没搞清楚,为创建概念练习开 issue 的最佳方式是什么。

在有些学习路径里,我们试过为概念本身和练习分别创建单独的 issue。 在另一些学习路径里,我们试过开那种带清单、可以逐项完成的 issue。 总的来说,我们觉得这样还是太让人望而生畏,希望能找到更好的办法。

你开始创建 issue 时,请和我们聊聊这个流程,我们会尽力帮你一起想清楚该怎么推进。

等我们找到更好的办法,会更新这份文档。