We already mentioned that functions are critical in performing problem decomposition. Beyond problem decomposition, functions are valuable in software for a number of other reasons, including:
Cognitive Load. You may have heard of cognitive load [1] before. It’s the amount of information your brain can handle at any given time and still be effective. If you are given four random words and asked to repeat them back, you might be able to do that. If you are given the same task with 20 words, most of us would fail as it’s too much information to handle at once. Similarly, if you’ve ever been on a road trip with your family and are trying to optimize the travel time, combined with stops for the kids, lunch breaks, bathroom stops, gas station stops, good locations for hotels, and so on, you might have felt your head swimming to manage all those constraints at once. That point when you can’t handle it all at once is when you’ve exceeded your own brain’s processing power. Programmers have the same problem. If they are trying to do too much at once or solve too complex a problem in one piece of code, they struggle to do it correctly. Functions are designed to help programmers avoid doing too much work at once.
Avoid Repetition. Programmers (and, we’d argue, humans in general) aren’t very excited about solving the same problem over and over. If I write a function that can correctly compute the area of a circle once, I don’t need to write that code ever again. That means if I have two sections of my code that need to compute the area of a circle, I’d write one function that computes the area of the circle and then I’d have my code call that function in each of those two places.
Improve Testing. It's a lot harder to test a section of code that does multiple things compared to code that does one thing. Programmers at companies use a variety of testing techniques, but a key technique is known as unit testing. Every function takes some input and produces some output. For a function that computes the area of a circle, for example, the input would be the circle’s radius and the output would be its area. Unit tests give a function an input and then compare that input to the desired result. For the area-of-a-circle function, we might test it by giving it varying inputs (e.g., some small positive numbers, some large positive numbers, and 0) and compare the result of the function against the values we know to be correct. If the answers from the function match what we expect, we have a higher degree of confidence that the code is correct. If the code produces a mistake, we won’t have much code to check to find and fix the problem. But if a function does more than one task, it vastly complicates the testing process as you need to test each task and the interaction of those tasks.
Improve Reliability. When we write code as experienced software engineers, we know we make mistakes. We also know Copilot makes mistakes. If you imagine you are an amazing programmer and each line of code you write is 95% likely to be correct, how many lines of code do you think you can write before at least one of those lines is likely to be incorrect? The answer ends up being only 14. We think 95% correctness per line is probably a high bar for even experienced programmers and is likely a higher bar than what Copilot produces. By keeping the tasks small, things solvable in 12-20 lines of code, we reduce the likelihood that there’s an error in the code. If combined with good testing as noted above, we can feel even more confident that the code is correct. Lastly, nothing is worse than code that has multiple mistakes that interact together, and the likelihood of multiple mistakes grows the more code you write. Both of us have been on multi-hour debugging expeditions because our code had more than one mistake and, for both of us, we became a lot better at frequent testing of short pieces of code as a result! Improve Code Readability. In this book, we’re going to mostly use Copilot to write code from scratch. But that’s not the only way to use Copilot. If you have a larger piece of software that you or your coworkers are all editing and using, Copilot can jump in to help write code for that, too. It’s in everyone’s interest to understand the code, whether most of it is written by humans or by Copilot. That way, we can find bugs more easily, determine what code to start modifying when we want to add new features, and understand at a high level what would be easy or difficult to achieve with our overall program design. Having tasks broken down into functions helps us understand what each part of the code is doing so we can gain a better understanding of how it all works together. It also helps divide up the work and responsibility for ensuring the code is correct.
These benefits are huge for programmers. Programming languages haven’t always had functions. But even before they did, programmers did their best to use other features to mimic functions. They were ugly hacks (Google “goto statements” if you’re interested), and all programmers are happy that we have proper functions now.
You may be asking, “I see how these advantages matter to humans, but how do they impact Copilot?” In general, we believe all the principles that apply to humans apply to Copilot, albeit for sometimes different reasons. Copilot may not have cognitive load, but it’s going to do better when we ask it to solve problems similar to what’s been done by humans before; since humans write functions to solve tasks, Copilot will mimic that and write functions as well. Once we’ve written and tested a function, whether by hand or by Copilot, we don’t want to write it again. Knowing how to test if your program is working properly is just as essential for code produced by humans as it is by Copilot. Copilot is as likely to make mistakes when it generates code, so we want to catch them quickly, just as we do with human-written code. And even if you only work on your own code and never have anyone else read it, as programmers who have had to go back to edit code we wrote years ago, let us tell you that it is important for your code to be readable, even if the only person reading it is you.
3.2 Benefits of Functions
3.2 函数的益处
We already mentioned that functions are critical in performing problem decomposition. Beyond problem decomposition, functions are valuable in software for a number of other reasons, including:
我们已经谈到,函数在进行问题分解方面发挥着关键作用。其实,函数在软件开发中之所以宝贵,还源于其他诸多方面的原因,包括:
Cognitive Load. You may have heard of cognitive load [1] before. It’s the amount of information your brain can handle at any given time and still be effective. If you are given four random words and asked to repeat them back, you might be able to do that. If you are given the same task with 20 words, most of us would fail as it’s too much information to handle at once. Similarly, if you’ve ever been on a road trip with your family and are trying to optimize the travel time, combined with stops for the kids, lunch breaks, bathroom stops, gas station stops, good locations for hotels, and so on, you might have felt your head swimming to manage all those constraints at once. That point when you can’t handle it all at once is when you’ve exceeded your own brain’s processing power. Programmers have the same problem. If they are trying to do too much at once or solve too complex a problem in one piece of code, they struggle to do it correctly. Functions are designed to help programmers avoid doing too much work at once.
认知负荷。你可能听说过“认知负荷”[1]。它指的是大脑在同一时间内能够有效处理的信息量。例如,给你四个随机单词让你重复,你可能没问题;但如果是20个单词,大多数人会感到难以一次性记住。同样,在家庭自驾游中,当你试图同时考虑旅行时间、孩子的休息点、午餐、如厕、加油以及酒店位置等因素时,你可能会感到难以应对。这就是你的认知负荷达到极限的时刻。程序员在编程时也会遇到同样的问题。如果他们试图一次完成太多任务或解决过于复杂的问题,很容易出错。因此,函数的设计就是为了帮助程序员避免一次性承担过多工作。
Avoid Repetition. Programmers (and, we’d argue, humans in general) aren’t very excited about solving the same problem over and over. If I write a function that can correctly compute the area of a circle once, I don’t need to write that code ever again. That means if I have two sections of my code that need to compute the area of a circle, I’d write one function that computes the area of the circle and then I’d have my code call that function in each of those two places.
避免重复。 程序员(我们认为,一般人也是如此)并不喜欢一遍又一遍地解决同一个问题。如果我编写了一个函数,它能正确计算圆的面积,那么我就不需要再次编写那段代码了。这意味着,如果我的代码中有两处需要计算圆的面积,我会编写一个计算圆面积的函数,然后在这两端代码中分别调用这个函数。
Improve Testing. It's a lot harder to test a section of code that does multiple things compared to code that does one thing. Programmers at companies use a variety of testing techniques, but a key technique is known as unit testing. Every function takes some input and produces some output. For a function that computes the area of a circle, for example, the input would be the circle’s radius and the output would be its area. Unit tests give a function an input and then compare that input to the desired result. For the area-of-a-circle function, we might test it by giving it varying inputs (e.g., some small positive numbers, some large positive numbers, and 0) and compare the result of the function against the values we know to be correct. If the answers from the function match what we expect, we have a higher degree of confidence that the code is correct. If the code produces a mistake, we won’t have much code to check to find and fix the problem. But if a function does more than one task, it vastly complicates the testing process as you need to test each task and the interaction of those tasks.
提升测试效率。 相较于执行单一功能的代码,测试同时执行多个任务的代码要复杂得多。程序员们在公司中运用多种测试方法,但其中关键的一种称为“单元测试”。每个函数接收一些输入并产生一些输出。例如,计算圆面积的函数,其输入就是圆的半径,输出则是面积。单元测试向函数提供输入,然后将这些输入产生的输出与预期结果进行比较。对于计算圆面积的函数,我们可以给它多种输入(比如一些小的正数、一些大的正数和0),并将其结果与我们已知的正确值进行对比。如果函数的输出与我们的预期匹配,我们就对代码的正确性更有信心。如果代码出现错误,我们也不用检查太多代码来找出并解决问题。但如果一个函数执行多个任务,这将使测试过程变得复杂,因为你需要测试每个任务以及它们之间的交互。
Improve Reliability. When we write code as experienced software engineers, we know we make mistakes. We also know Copilot makes mistakes. If you imagine you are an amazing programmer and each line of code you write is 95% likely to be correct, how many lines of code do you think you can write before at least one of those lines is likely to be incorrect? The answer ends up being only 14. We think 95% correctness per line is probably a high bar for even experienced programmers and is likely a higher bar than what Copilot produces. By keeping the tasks small, things solvable in 12-20 lines of code, we reduce the likelihood that there’s an error in the code. If combined with good testing as noted above, we can feel even more confident that the code is correct. Lastly, nothing is worse than code that has multiple mistakes that interact together, and the likelihood of multiple mistakes grows the more code you write. Both of us have been on multi-hour debugging expeditions because our code had more than one mistake and, for both of us, we became a lot better at frequent testing of short pieces of code as a result! Improve Code Readability. In this book, we’re going to mostly use Copilot to write code from scratch. But that’s not the only way to use Copilot. If you have a larger piece of software that you or your coworkers are all editing and using, Copilot can jump in to help write code for that, too. It’s in everyone’s interest to understand the code, whether most of it is written by humans or by Copilot. That way, we can find bugs more easily, determine what code to start modifying when we want to add new features, and understand at a high level what would be easy or difficult to achieve with our overall program design. Having tasks broken down into functions helps us understand what each part of the code is doing so we can gain a better understanding of how it all works together. It also helps divide up the work and responsibility for ensuring the code is correct.
提升可靠性。 作为资深软件工程师,我们在编码时难免会犯错。Copilot 同样不是完美无缺。设想你是一位杰出的程序员,你写出的每一行代码都有 95% 的把握是正确的,那么你觉得你能连续写多少行代码,才可能首次出现错误?答案是仅仅 14 行。即便是对经验丰富的程序员而言,每行代码 95% 的正确率标准可能也过高,更何况 Copilot。通过限制任务的规模,控制在 12 至 20 行代码之内解决问题,我们就能减少出错的几率。结合前文提到的有效测试,我们对代码正确性的信心将更加充足。最后,最糟糕的情况莫过于代码中存在多个相互影响的错误,而代码量越大,出错的概率也随之增加。我们都曾因为代码中的多个错误而陷入长时间的调试困境,这使得我们更加擅长于对小块代码进行频繁测试!增强代码可读性。本书将主要采用 Copilot 来全新编写代码,但这并非 Copilot 唯一的使用场景。当你和同事们共同编辑和使用一个大型软件项目时,Copilot 也能助一臂之力。无论是人类还是 Copilot 编写的代码,理解它对每个人都至关重要。这有助于我们更轻松地发现并修复错误,确定何时开始调整代码以添加新功能,并在宏观层面上把握实现我们整体程序设计的难易程度。将任务划分为函数,有助于我们理解代码的各个部分各自承担的职责,从而更好地理解它们是如何协同作用的。这也有助于我们分配工作和责任,确保代码的准确性。
These benefits are huge for programmers. Programming languages haven’t always had functions. But even before they did, programmers did their best to use other features to mimic functions. They were ugly hacks (Google “goto statements” if you’re interested), and all programmers are happy that we have proper functions now.
这些益处对于程序员来说极为重要。编程语言并不是从一开始就内置了函数功能。但在函数功能出现之前,程序员们已经尽力使用其他特性来模拟函数的效果。那些替代方案往往显得笨拙(如果你感兴趣,不妨在Google中搜索“goto语句”了解一下),现在我们有了正规的函数,所有程序员都为之庆幸。
You may be asking, “I see how these advantages matter to humans, but how do they impact Copilot?” In general, we believe all the principles that apply to humans apply to Copilot, albeit for sometimes different reasons. Copilot may not have cognitive load, but it’s going to do better when we ask it to solve problems similar to what’s been done by humans before; since humans write functions to solve tasks, Copilot will mimic that and write functions as well. Once we’ve written and tested a function, whether by hand or by Copilot, we don’t want to write it again. Knowing how to test if your program is working properly is just as essential for code produced by humans as it is by Copilot. Copilot is as likely to make mistakes when it generates code, so we want to catch them quickly, just as we do with human-written code. And even if you only work on your own code and never have anyone else read it, as programmers who have had to go back to edit code we wrote years ago, let us tell you that it is important for your code to be readable, even if the only person reading it is you.
您可能会好奇,“我理解这些优势对人类极为重要,但它们对 Copilot 有何影响?”总体而言,我们相信,所有适用于人类的原则也同样适用于 Copilot,尽管背后的原理可能有所不同。Copilot 固然没有认知负荷,但当我们要求它处理与人类解决过的类似问题时,它的表现会更加出色;既然人类通过编写函数来完成任务,Copilot 也会仿效这一做法,同样编写函数。一旦我们通过手工或 Copilot 编写并测试了一个函数,我们就不希望再次重写它。了解如何验证程序是否运行正常,对于人类编写的代码和 Copilot 生成的代码来说,都是至关重要的。Copilot 在生成代码时同样可能犯错,因此我们希望能够迅速发现并修正这些错误,就像我们对待人类编写的代码一样。即便你只是在独立编写和维护自己的代码,从未让他人阅读过,但作为曾不得不回顾和修改多年前自己编写的代码的程序员,我们要告诉你,保持代码的可读性非常重要,哪怕唯一的读者只有你自己。