程序员应该学习的 10 件事
翻译:10 Things Software Developers Should Learn about Learning
原文:
https://cacm.acm.org/magazines/2024/1/278891-10-things-software-developers-should-learn-about-learning/fulltext本文接下来的 10 个部分提供了适用于软件开发人员的有关学习的研究支持发现,并讨论了它们的实际意义。这些信息可以帮助自己学习、教授初级员工和招聘员工。
1.人类的记忆不是bit组成的
记忆是学习的核心,“学习意味着一个人的长期记忆发生了变化”。
但人类记忆是复杂的。对于计算机内存,我们可以读取和写入数据,读取时不会改变数据。但是人类记忆的过程不那么纯粹,在读取时会发生重新整合的过程。
人类记忆的另一个特征是“传播激活”。当我们视图记住某件事情时,会激活神经元通路来访问目标信息。传播激活对记忆有负面影响,对解决问题有积极影响。传播激活意味着相关但不精确的信息可能与目标信息混为一谈,这意味着我们对信息的回忆可能不可靠。另一方面,传播会连接不同的区域,产生创造性的问题解决方案。
2.人类的记忆是由一个有限系统和一个无限系统组成的
人类记忆包括与学习相关的两个主要组成部分:长期记忆和工作记忆。长期记忆是信息永久存储的地方,并且在功能上是无限的,有点像磁盘。工作记忆用于有意识地推理信息以解决问题,类似于CPU寄存器,实时存储有限数量的信息,以允许访问和操作。 工作记忆能存储的信息很有限,通常工作记忆容量与智力有关,能加快学习速度。 长期记忆则决定我们最终掌握的知识。
当人们对某个主题了解得更多时,他们会将一些信息串联成一个块(chunk)。例如,如果你对电子邮件很熟悉,就会把gmail.com
当成是一个东西,而不是8个字符。通过练习我们能够自然地把信息串联成块,增加工作记忆。
在学习新工具或技能时,重要的是要了解任务所需的认知负荷或工作记忆容量。
认知负荷分为两部分:内在负荷和外在负荷。内在负荷是完成任务本身需要多少条信息或块;除非更改任务,否则无法更改它。相比之下,外在负荷是不必要的信息,但它是执行任务的一部分。演示格式是外在负荷如何变化的一个例子。如果要实现数据库模式,则使用带有表和属性的图比使用一堆文字描述更容易理解。看一堆文字更费劲,也就是有更高的外在负载,因为您必须将文字描述转换为模式(schema),而关系图可以直接映射过去(见图 1)。对于初学者来说,外来负荷通常更高,因为他们很难区分内在信息和外来信息。
当任务超出个人能力的时,要认识到可以通过分解任务来解决。将问题分解成可以处理和分块的更小的部分,最终将使人们能够解决复杂的问题。
3.专家把握整体,新手陷入细节
专家可以通过经验,快速发现程序代码中的常见模式来理解代码。 其中一个例子是编程中的“设计模式”,类似于前面讨论的块。专家可能会立即意识到特定代码段正在执行排序算法,而初学者可能会逐行阅读以尝试理解代码的工作原理,而没有去识别更大的图景。
由此得出的推论是,初学者可以通过阅读和理解大量代码成为专家。专家们建立了一个模式的心理库,让他们在未来更容易地读写代码。看到纯粹的命令式 C 代码可能只部分适用于函数式 Haskell 代码,因此看到各种编程范式将进一步有所帮助。总的来说,这种模式匹配是阅读和使用更多代码和更多类型的代码将提高编程熟练程度的原因。
4. 理解一个概念从抽象到具体再到抽象
专家使用通用和抽象的术语来寻找基本概念,而不关注细节,而初学者则专注于表面细节。
例如,当向刚接触这个概念的人解释 Python 中的可变参数函数时,专家可能会说它是一个可以接受不同数量参数的函数。初学者可能会关注细节,例如声明和调用函数的确切语法,并可能认为传递一个参数是一种特殊情况。专家在向他们解释概念时可能更容易理解或预测细节。
当你学习一个新概念时,你将从两种形式的解释中受益:抽象的特征和具体的细节和例子。更具体地说,您将受益于语义波,这是澳大利亚科学家 Karl Maton 定义的概念,如图2所示。
图2.可变参数函数的语义波。
在语义波之后,您可以在抽象定义和概念的几个不同示例之间不断切换。例子越多样化越好。即使是错误的例子也是有益的,可以理解它们为什么是错误的, 例如在尝试了解什么是常量时看到标记为非常量的可变变量。此过程称为解包。
通过这些不同的示例,您可以(重新)访问抽象定义并构建对概念的更深入理解。更深入的理解源于认识到示例中的多个细节如何连接到定义中的一个抽象概念,这个过程称为重新打包。
编程经常涉及学习抽象概念。面对一个需要学习的抽象概念,例如函数,人们经常会伸手去研究这个概念的具体实例,例如,返回数字绝对值的 abs 函数。 一个挑战是,随着概念变得更加抽象(从值到变量/对象到函数/类,再到高阶函数/元类,最终是范畴论),与具体示例的距离就会增加。幸运的是,当我们学习抽象概念时,它们对我们来说变得更加具体。最初,函数是一个抽象的概念,但经过大量练习,函数对我们来说变成了一个具体的项目(或块),我们可以学习下一个层次的抽象。
5. 间距和重复问题
你有多少次听说过你不应该为考试而死记硬背呢?当然,除非你希望在第二天就把一切都忘记。这个建议基于认知心理学中最可预测和最持久的效应之一:间隔效应。根据间隔效应,人类通过将实践分散在多个会话、多个天数,甚至多个星期中学习问题解决概念效果最好。
间隔效应起作用的原因是由于本文前面描述的长期记忆和工作记忆之间的关系。当学习者练习解决问题时,他们会练习两种技能。首先,将问题中的信息与可以解决问题的概念(例如过滤循环)相匹配,其次,应用该概念来解决问题(例如编写循环)。第一项技能需要激活长期记忆中通往概念的正确神经通路。 如果学习者反复解决同一种问题,例如for each循环问题,那么通往长期记忆的途径就会保持活跃,他们就会错过练习第一项技能。无间隔实践的一个常见结果是,人们可以解决问题,但前提是他们被告知使用哪个概念。虽然交错不同类型的问题(例如循环和条件问题)可能会有所帮助,但路径需要时间才能恢复到基线,因此必须间隔以充分利用练习时间。 此外,大脑需要休息来巩固已经处理过的新信息,以便将其应用于新问题。
与这一久经考验的原则相反,密集的编码训练营要求学习者将他们的解决问题的练习塞进不间隔的课程中。虽然这并不理想,但间距效应的研究人员从一开始就知道,大多数学习者仍然喜欢将他们的练习塞进尽可能短的时间内。对于那些学习编程的唯一可行选择是强化训练营的人,我们可以应用间隔效应来最大化他们的成果。
为了安排一天的学习,学习者应将学习时间限制在 90 分钟或更短。 大脑中的神经化学平衡使得在此之后难以集中注意力。每次学习后,至少休息20分钟。 真正休息的是散步或安静地坐着——不要做其他任务、闲着地浏览互联网或与他人聊天。休息会加速巩固过程,睡眠也有巩固记忆的效果。
在学习回合中,有几种策略可以最大限度地提高效率。首先,随机化正在解决的问题类型的顺序,以便在长期记忆中激活不同的概念。 不过,请注意,随机化顺序可以改善学习成果,但需要更多的努力。 第二种策略是随机休息一下,以增强记忆巩固。建议每 2-5 分钟休息 10 秒。
6. 互联网并没有让学习过时
自从搜索引擎出现以来,尤其是最近出现的ChatGPT、Copilot等大语言模型,某个语法或者函数的使用方法很容易在几秒钟得到,那么我们还需要去学习细节或者任何东西吗?
我们通过将知识片段存储在我们的长期记忆中并在它们之间建立联系来学习。如果知识不存在于大脑中,因为你还没有学好它,大脑就无法在它之间形成任何联系,因此不可能有更高层次的理解和抽象。如果每次需要一段代码来执行数据库连接时,您都在网上搜索它,插入它,然后继续前进,那么您将不太可能学到太多有关连接的知识。依赖互联网或人工智能的智慧在初学者和专家之间有所不同:从未学习过细节因此缺乏记忆联系的初学者与已经学习了更深层次结构但寻找被遗忘的细节的专家之间存在关键区别。
甚至有一些证据表明,在互联网上搜索记忆信息的效率较低。一项研究发现,如果通过互联网找到信息(与实体书相比),则记忆力较差。 另一项研究发现,与在诉诸搜索之前首先尝试思考答案相比,立即搜索互联网会导致以后对相同信息的回忆更差。 似乎搜索可能会剥夺大脑回忆信息增强记忆效应的好处。
还有前面讨论的认知负荷问题。互联网搜索需要一种大脑的上下文切换形式;其有限的注意力和工作记忆必须从手头的任务(编程)切换到新的认知任务(搜索互联网并选择结果或评估人工智能生成的结果)。如果记住了所需的知识,那么访问速度不仅要快得多(就像使用缓存而不是从硬盘获取),而且还避免了上下文切换和从搜索中过滤掉无关信息的认知消耗。因此,尽管信息可以在互联网上找到,但记住信息的原因有很多。
7. 解决问题不是通用技能
解决问题是编程的很大一部分。软件开发中一个常见(但不正确)的想法是直接将解决问题作为一种特定技能来教授,然后可以将其应用于开发的不同方面(设计、调试等)。因此,解决问题被(错误地)视为一种通用技能。然而,这不是解决问题在大脑中的工作方式。
虽然人类确实有一些通用的问题解决技能,但它们的效率远不如特定领域的问题解决技能,例如能够调试程序。虽然我们可以学会推理,但我们并没有学会如何解决一般的问题。相反,我们学习如何解决编程问题,或者如何计划最好的国际象棋动作,或者如何创建编织图案。这些技能中的每一个都是独立的,不会影响其他技能。对国际象棋的研究发现,学习国际象棋对其他学术和认知技能的影响很小或没有影响,音乐教学和认知训练也是如此。 这种无法转移解决问题的能力是“大脑训练”对发展一般智力无效的原因。
这条规则的一个例外似乎是空间技能。空间技能使我们能够在脑海中想象物体,如俄罗斯方块形状,并在脑海中操纵这些物体,如旋转俄罗斯方块形状。培训这些通用技能可以改善其他学科的学习。这种现象是如此不寻常,以至于在认知和学习科学中引起了很大的恐慌。 然而,无论初始能力、年龄或训练任务类型如何,空间训练都能提高一系列非语言技能的表现。 最近的研究甚至表明,空间训练可以提高专业软件开发人员的效率,这可能是因为他们仍在学习新概念。 即使有这个奇怪的例外,学习如何解决编程问题的最佳方法仍然是练习解决编程问题,而不是从学习国际象棋或其他认知训练中寻找性能优势。
这里对招聘有次要影响。筛选编程候选人的一个流行想法是提供脑筋急转弯的谜题,例如如何称量一架大型喷气式飞机。正如谷歌在2013年所发现的那样,这是在浪费时间 ——脑筋急转弯世界中的问题解决和编程世界中的问题解决之间没有可靠的对应关系。如果要判断编程能力,就要评估编程能力。
8. 在某些情况下,专业知识可能会有问题
我们已经讨论了专业知识有利于学习和绩效的许多方式。但是,成为专家也会导致问题。
程序员使用工具和辅助工具来提高效率,例如版本控制系统或 IDE。这些工具可以对初学者和专家产生不同的影响。初学者可能会被专业工具中可用的选项数量所淹没(由于认知负荷增加),并且可能会从有关如何使用该工具的初学者友好提示中受益。然而,专家们发现同样的提示比有用更分散注意力,因为他们已经知道该怎么做。这被称为专业知识逆转效应:帮助初学者的提示和指南可能会妨碍专家并降低他们的工作效率。
程序员通常在其整个职业生涯中学习多种编程语言。一旦掌握了多种语言,了解它们可能是有益的,但有时将知识从一种编程语言转移到另一种编程语言会导致知识错误。例如,程序员可以学习 Java 中的继承,其中只要签名匹配,一个方法就会覆盖父方法,并将此知识转移到 C++,其中重写父方法还需要将父方法声明为虚拟方法。这些差异——语言之间的特征在语法上相似,但在语义上不同——特别阻碍了知识的转移。
专家经常帮助培训初学者,但没有培训他人经验的专家往往没有意识到初学者的想法不同。因此,他们无法为具有不同心智模式的人量身定制解释。这被称为专家盲点问题:一旦成为专家,就很难通过初学者的眼睛看东西。通过仔细聆听初学者解释他们目前的理解并相应地调整解释,可以克服它。
然而,有时知识变得如此自动化,以至于专家很难用语言表达出来。 这种自动化知识就是为什么专家们对如何解决问题或将他们的过程解释为“我只知道”的直觉。在这些隐性知识的情况下,初学者可能更好地从旨在支持初学者的教学材料中学习,通常称为脚手架教学,或者从同行而不是专家那里学习。知识渊博(但仍然相对新手)的同伴是弥合初学者和专家之间差距的非常有价值的资源。它们可以帮助初学者开发新知识,帮助专家重新发现自动化知识。
9. 编程能力的预测因素不明确
与大多数活动一样,学习编程的成功是建立在内在能力和实践的结合之上的。有些人认为这纯粹是关于能力——“你天生就有”的观点——有些人认为这几乎完全是关于实践——“10,000小时”的想法,即专业知识只需要足够的练习。这两种极端观点都是错误的,在本节中,我们将探讨能力和实践的不同影响的证据。
有很多研究试图预测编程能力,但可靠的结果很少。对编程能力进行预测测试的尝试通常都以失败告终。研究发现,以下所有因素都无法预测编程能力:性别、年龄、学术专业、种族、以前的数学表现、以前使用另一种编程语言的经验、对 CS 的看法以及对人文或科学的偏好。 1960 年代开始有一个编程能力测试行业,但正如罗宾斯 总结的那样,预测准确性很差,测试不再使用。
关于多年经验的重要性,有好坏参半的证据,这与实践有关。程序员在 Stack Overflow 上的声誉与他们的年龄之间存在相关性:老年人的声誉更高。 然而,最近的一项研究发现,在职业生涯相对较早的程序员中,多年的经验和编程任务的成功之间只有微弱的联系,这表明能力可能比经验有更强的影响, 31 至少在程序员职业生涯的早期是这样。
与大多数领域一样,弱预测早期编程成功的两个因素是一般智力和工作记忆容量。 这些因素大致代表了推理能力以及学习者一次可以处理多少信息。因此,他们预测的是学习速度,而不是绝对能力。这两个因素的一个子指标,即空间推理,是编程成功的更有力的预测指标,尽管仍然相当温和。 空间推理也预示着其他科学和数学领域的成功, 所以这不是特定于编程的。此外,由于各种原因,这些弱到中度的相关性随着经验的增加而基本消失。因此,聪明的人并不总是能成为好的程序员,而好的程序员也不一定具有很高的一般智力。
简而言之,很难预测谁将能够编程,尤其是从长远来看。程序员可以来自任何背景或人口统计学,与任何其他因素(如智力)的联系在经验面前通常是转瞬即逝的。因此,在招聘新程序员时,没有识别编程能力的捷径,也没有任何可靠的“候选人档案”来筛选编程能力的候选人。
10. 你的心态很重要
在学习新技能或培训某人掌握新技能时,请记住,以成长型思维方式处理任务是有效的,但也是一项需要培养的技能。不幸的是,我们不能简单地告诉人们要有成长的心态并获得好处。相反,通过寻求或提供有关学习过程和策略有效性的诚实反馈来培养这种技能。对于导师来说,表扬男球员取得进步的领域,并接受他们会犯错误而不责备他们。对于学习者,当您对自己的进步有疑问时,反思过去几周或几个月的技能是如何提高的。此外,在面对失败时,预计成长型思维模式会转向固定型思维模式,但它也可以通过实践重新发展并变得更强大。感到气馁是正常的,但这并不意味着你会一直感到气馁。如果您想戒烟,请休息一下,散散步,考虑您的策略,然后再试一次。
总结
软件开发人员必须不断学习,以跟上该领域快节奏的变化。学习任何东西,包括编程,都涉及将项目提交到内存中。人类的记忆非常复杂。虽然它与计算机体系结构有一些相似之处,但有一些关键的区别使其工作方式完全不同。在这篇文章中,我们解释了目前对人类记忆如何运作、学习如何运作、初学者和专家之间的差异的科学理解,并将其与软件开发人员可以采取的实际步骤联系起来,以改善他们的学习、培训和招聘。
我们将建议分为招聘建议和培训学习建议。
对于招聘,我们提出以下建议:
编程能力没有好的替代指标。基于性别、种族或其他因素的刻板印象没有证据支持。如果你想知道候选人的编程能力如何,请查看他们以前的工作或测试他们的真实编程任务。强调一个具体的观点:不要用脑筋急转弯的谜题来测试候选人。
至少在年轻的开发人员中,多年的经验可能不是衡量能力的非常可靠的标准。
Behroozi等人提出的一个相关建议 是,在提出解决方案之前,让候选人在房间里自己解决面试问题,因为面试官在解决面试问题时观察或要求说话的额外压力会增加认知负荷和压力,从而损害表现。
对于学习和培训,我们提出以下建议:
阅读大量代码将帮助某人成为更高效的程序员。
专家并不总是最擅长培训初学者。
学习需要时间,包括学习课程之间的时间。激烈的死记硬背是无效的,但间隔重复是有效的。
同样,花时间远离问题可以帮助解决问题。
仅仅因为你可以通过互联网搜索或生成式人工智能工具找到它,并不意味着学习已经过时了。
使用示例在抽象概念和具体的可学习事实之间切换。
寻求成功(而不是避免失败)并相信能力是可以改变的,是适应力和学习的重要因素。
延伸阅读
Why Don’t Students Like School?
记忆和大脑工作原理
The Programmer’s Brain
学习、记忆原理在编程中的应用
How Learning Happens: Seminal Works in Educational Psychology and What They Mean in Practice
介绍了有影响力的论文